2013-03-05 63 views
2

我目前有一個方法,它讀取數據以確定是否需要更新,然後將更新推送到數據庫(注入依賴項)。該方法非常困難,我發現併發性相關的錯誤,即多個更新,因爲多個線程在第一次更新之前讀取數據。TransactionScope而不是使用鎖

我用鎖來解決這個問題,它工作的很好。我該如何使用TransactionScope來做同樣的事情?我可以嗎?它會鎖定另一個線程嗎?此外,我可以'鎖定'一個特定的'id',因爲我正在使用一個鎖(我保存一個Dictionary來存儲一個對象來鎖定每個id)?

我使用實體框架5,雖然它隱藏了一個存儲庫和工作單元模式。

+1

ReadWriterLockSlim呢?如果你有多個讀取和少量寫入,它比鎖定要好得多。 (如何使TransactionScope線程安全?) – 2013-03-05 16:36:16

+0

這將比只是鎖(+1)更好。但是,它不允許我在其他地方安全地處理數據,或者在另一臺機器上處理同一數據。 – ccook 2013-03-05 16:38:05

+1

ReaderWriterLockSlim通常建議使用_允許不允許遞歸訪問,並且如果允許的話,允許編寫有問題的代碼,這是一種危險的情況。就像他解決的問題一樣危險,如果不是更多的話。 – 2013-03-05 16:38:07

回答

2

應用程序級鎖定可能不是解決此問題的方法。首先,您通常只需要鎖定單個記錄或記錄範圍。接下來,您可能稍後需要鎖定其他修改並進入相當複雜的代碼。

這種情況通常用樂觀或悲觀併發處理。

  • 樂觀併發 - 您將有額外的數據庫生成列(數據庫通常具有像時間戳或行轉換那樣的特殊類型)。每次更新記錄時,數據庫都會自動更新該列。如果將此列配置爲行版本EF將在更新的where條件中包含該列=>執行的更新將搜索具有給定密鑰和行版本的記錄。如果找到記錄,它將被更新。如果未找到記錄,則表示記錄中不存在關鍵字或者其他人已更新記錄,因爲當前進程已加載其數據=>您將得到異常,您可以嘗試刷新數據並再次保存更改。此模式對於未更新太多的記錄很有用。在你的情況下,它可能會導致另一個麻煩。
  • 悲觀併發 - 此模式使用數據庫鎖定代替。當您查詢記錄時,您將鎖定它進行更新,以便其他人無法將其鎖定以便直接進行更新或更新。不幸的是,這種模式目前在EF中沒有直接支持,您必須通過原始SQL執行。我寫了一個article explaining the pessimistic concurrency及其與EF的用法。對於重負載下的數據庫,即使是悲觀併發可能也不是一個好的解決方案。

如果你真的建立地方很多併發進程的嘗試更新同一數據的所有可能與重新設計結束時的解決方案,因爲根據鎖定或重新運行失敗的更新將不會有可靠的高性能解決方案。

+0

感謝您的反饋!我目前所推出的是應用程序級鎖的組合(基於性能,如果TryEnter失敗,則會跳過'action'),其中存在應用程序作用域並使用isolationlevel.repeatableread來處理罕見的併發情況來自該方法之外的問題。不好的解決方案? – ccook 2013-03-05 19:57:22