2011-06-24 59 views
0

我有一個包含整數ID值的文件。目前讀取該文件與ReaderWriterLockSlim保護這樣:如何製作只允許一個線程從資源讀取的鎖定?

public int GetId() 
    { 
     _fileLock.EnterUpgradeableReadLock(); 
     int id = 0; 
     try { 
      if(!File.Exists(_filePath)) 
       CreateIdentityFile(); 

      FileStream readStream = new FileStream(_filePath, FileMode.Open, FileAccess.Read); 
      StreamReader sr = new StreamReader(readStream); 
      string line = sr.ReadLine(); 
      sr.Close(); 
      readStream.Close(); 
      id = int.Parse(line); 
      return int.Parse(line); 
     } 
     finally { 
      SaveNextId(id);  // increment the id 
      _fileLock.ExitUpgradeableReadLock(); 
     } 
    } 

的問題是,GetId()後後續行動可能會失敗。正如你所看到的,GetId()方法每次都會增加ID,不管發出ID後會發生什麼。發佈的ID可能會被掛起(如上所述,可能會發生異常)。隨着ID增加,一些ID可能未被使用。

所以我想移動SaveNextId(id),刪除它(SaveNextId()實際上也使用鎖,除了它是EnterWriteLock)。在完成所有必需的方法執行後,從外部手動調用它。這帶來了另一個問題 - 在執行SaveNextId()之前,多個線程可能會輸入GetId()方法,並且它們可能都會收到相同的ID。

我不希望任何解決方案,在操作後必須更改ID,並以任何方式糾正它們,因爲這樣做不好,可能會導致更多問題。

我需要一個解決方案,我可以以某種方式回調到FileIdentityManager(這是處理這些ID的類),並讓管理員知道它可以執行保存下一個ID然後釋放包含文件的讀鎖定ID。

Essentialy我想複製關係數據庫的自動增量行爲 - 如果在行插入過程中出現任何錯誤,這個ID不被使用,它仍然可以使用,但它也不會發生相同的ID發出。希望這個問題是可以理解的不足,爲您提供一些解決辦法..

UPDATE:請參見注釋給答案有關行爲的詳細信息我想

+0

我認爲你有非順序的ID是一件壞事嗎? – Chris

+0

如果你想要一個只允許一個線程的鎖,爲什麼不使用'lock'?我只是閱讀標題而不是問題! –

+0

我會處理因刪除而發生的問題。但我不想把它們浪費在沒有成功的文件保存上,沒有成功的類型轉換等等。我現在就開始工作了,但這個非序列ID問題很快就顯示出來了,因爲ID暴露在UI中,它看起來很難看。兩個併發內容項目可能具有相隔10個數字或更多的ID。 – mare

回答

2

Essentialy我想複製 關係數據庫自動增量 行爲 - 如果有什麼行插入時錯誤的ID是不 使用,它仍然是AV可供使用 但它也不會發生 相同的ID發佈。希望 的問題是可以理解的,你可以提供一些解決方案。

一般來說,這不是我觀察到的行爲。當您在事務內部使用自動增量向表中插入一行並將其回滾時,您已丟失該ID。

所以在我看來,你實施這種方式是正確的行爲。

更新 你能保證你的唯一辦法「不想浪費他們不成功的文件保存,不成功的類型轉換,等等。」在你請求一個新的ID直到你的保存完成並且未能將增量回滾到ID的時候,改變你的阻塞代碼的範圍以阻止它。

這將大大減少並行可以達到的水平。

如果你想保持並行性的潛力更高,你應該檢查你的一切,你可以要求例如一個ID之前檢查類型和格式錯誤。

顯然,有些事情就像外部錯誤(IO異常),你根本無法做任何事情。

+0

好吧,這可能是真的我想(應該去檢查一遍),但現在我真的想解決我的問題,因爲它應該是可能的,特別是因爲我們控制行爲,在SQL Server中我們不能做太多的事情它... – mare

+0

好吧,我現在明白你好一點了。我決定使用@WraithNath建議的鎖,但在存儲庫中執行。鎖塊將包含try..catch塊,try將包含GetId(),其他調用和SaveNextId()。使用ReaderWriterLockSlim,GetId()和SaveNextId()將保持現在的狀態。但是,我有一個問題。這是如何影響的,因爲這是一個ASP.NET MVC應用程序,如果你知道我的意思,我也使用Ninject創建具有InRequestScope()選項的存儲庫。 – mare

2
private static readonly object _lock = new object(); 

    public int GetId() 
    { 
     lock(_lock) 
     { 
     //You code to get ID here 
     } 
    } 
+0

我猜想當代碼獲得ID完成鎖立即解鎖和另一個線程可以進入?可能在我們知道發生了什麼事情之前呢? – mare

+0

是的,當代碼運行在鎖範圍之外時,代碼將解鎖並允許另一個線程進入。你可以在鎖裏放入任何你想要的邏輯來計算你的下一個id是什麼,以及是否保存它。但只要在整個應用程序中使用此方法,一次只能有一個線程通過它。 – WraithNath

+0

事情是我的GetId()方法從處理數據訪問和插入的存儲庫中調用。它從FileIdentityManager類請求一個ID,然後構造將被序列化爲XML的對象(包括ID分配)。在序列化過程中發生異常時,可以回滾ID。 – mare

0

您在數據庫中看到的行爲是可能的,因爲ID生成和插入行是原子的。如果你想在你的應用程序中有這種行爲,那麼我建議你在存儲數據之前立即獲取ID。這會將您的「交易範圍」降低到最小的窗口,並且應該防止任何異常干擾。

如果由於某些原因不好這是不可能的,另一種可能是有一個「ID經紀人」該緩存ID計數器。它會從文件讀取當前計數器,將其增加一些數字(比如100),然後通過單線程方法將連續的ID分配給所有的調用者。當它發佈了全部100個時,它會再次更新文件。在關機時,它會最後一次使用它發出的最後一個值寫入文件。唯一的問題就是如果你的系統崩潰了,你的ID會出現一個缺口,但是有辦法彌補這個缺陷。

相關問題