2009-06-07 111 views
12

它是什麼以及如何使用?是否有一個保證C#中FIFO順序的同步類?

我需要,因爲我有一個計時器,每秒鐘插入數據庫,並且我有定時器處理程序和主線程之間的共享資源。 我想保證,如果定時器處理程序在插入中需要超過一秒鐘,等待的線程應該按順序執行。 這是我的定時器處理

private void InsertBasicVaraibles(object param) 
{ 
      try 
      { 
       DataTablesMutex.WaitOne();//mutex for my shared resources 
       //insert into DB 
      } 
      catch (Exception ex) 
      { 
       //Handle 
      } 
      finally 
      { 
       DataTablesMutex.ReleaseMutex(); 
      } 
} 

樣本代碼,但目前的互斥體不保證任何順序。 我把詳細的問題後沒有答案!

+0

定義鎖是FIFO。一條線進去了,沒有人進來,直到它出去! – 2009-06-07 13:19:35

+10

@Mitch:但不能保證必須等待的第一個線程是之後立即解除阻塞的線程。所以不,鎖不是FIFO。 – 2009-06-07 13:20:23

+2

(即,如果T1當前持有該鎖,則T2嘗試獲取它,然後T3嘗試獲取它,但不能保證T3在T2之前獲得它)。 – 2009-06-07 13:20:56

回答

26

你需要編寫自己的類來做到這一點,我發現這個例子中(粘貼,因爲它看起來好像網站的域名已失效):使用的

using System.Threading; 

public sealed class QueuedLock 
{ 
    private object innerLock; 
    private volatile int ticketsCount = 0; 
    private volatile int ticketToRide = 1; 

    public QueuedLock() 
    { 
     innerLock = new Object(); 
    } 

    public void Enter() 
    { 
     int myTicket = Interlocked.Increment(ref ticketsCount); 
     Monitor.Enter(innerLock); 
     while (true) 
     { 

      if (myTicket == ticketToRide) 
      { 
       return; 
      } 
      else 
      { 
       Monitor.Wait(innerLock); 
      } 
     } 
    } 

    public void Exit() 
    { 
     Interlocked.Increment(ref ticketToRide); 
     Monitor.PulseAll(innerLock); 
     Monitor.Exit(innerLock); 
    } 
} 

例子:

QueuedLock queuedLock = new QueuedLock(); 

try 
{ 
    queuedLock.Enter(); 
    // here code which needs to be synchronized 
    // in correct order 
} 
finally 
{ 
    queuedLock.Exit(); 
} 

Source via Google cache

1

上有沒有保證的順序任何內置的同步對象:http://msdn.microsoft.com/en-us/library/ms684266(VS.85).aspx

如果你想有一個保證的順序,你必須嘗試自己建立的東西,但請注意,這不是那麼容易,因爲它聽起來特別是當多個線程在同一時間(接近)達到同步點時。在一定程度上,他們將被釋放的順序將始終是「隨機的」,因爲你無法預測點到達的順序,所以它真的很重要?

10

剛剛閱讀Joe Duffy的「Windows上的並行編程」聽起來像你會通常從.NET監視器獲得FIFO行爲,但有些情況下不會發生這種情況。本書的內容說:「由於監視器在內部使用內核對象,它們表現出與OS同步機制同樣表現出的粗略FIFO行爲(在前一章中描述過)。監視器是不公平的,所以如果另一個線程在喚醒的等待線程嘗試獲取鎖之前偷偷摸摸並獲得鎖,允許偷偷摸摸的線程獲取鎖。「

我不能立即找到引用「在前面的章節」的部分,但它確實注意,鎖定已作出明顯不公允的Windows更新的版本,以提高可擴展性和降低鎖定車隊

您是否確實需要鎖定爲FIFO?也許有不同的方法來解決這個問題。我不知道.NET中有哪些鎖定保證是FIFO。

7

你應該重新設計你的系統不依賴於線程的執行順序。例如,不是讓你的線程進行一次可能需要一秒以上的數據庫調用,而是讓你的線程將他們要執行的命令放入一個數據結構中,如隊列(或者如果有某個東西說「這個應該在另一個之前「)。然後,在業餘時間,排空隊列,並按照正確的順序逐個插入一個。

1

其實答案都不錯,但我通過刪除計時器解決了這個問題,並(之前定時器處理程序)運行的方法進入後臺線程如下

private void InsertBasicVaraibles() 
    { 
     int functionStopwatch = 0; 
     while(true) 
     { 

      try 
      { 
      functionStopwatch = Environment.TickCount; 
      DataTablesMutex.WaitOne();//mutex for my shared resources 
      //insert into DB 
      } 
      catch (Exception ex) 
      { 
      //Handle    
      } 
      finally    
      {     
       DataTablesMutex.ReleaseMutex(); 
      } 

      //simulate the timer tick value 
      functionStopwatch = Environment.TickCount - functionStopwatch; 
      int diff = INSERTION_PERIOD - functionStopwatch; 
      int sleep = diff >= 0 ? diff:0; 
      Thread.Sleep(sleep); 
     } 
    } 
0

跟進馬修·布林德利的答案。

如果從

lock (LocalConnection.locker) {...} 

轉換代碼,那麼你既可以做了IDisposable或做我所做的:

public static void Locking(Action action) { 
    Lock(); 
    try { 
    action(); 
    } finally { 
    Unlock(); 
    } 
} 

LocalConnection.Locking(() => {...}); 

我決定不IDisposable的,因爲它會在每次調用創建一個新的不可見對象。

至於重入問題,我修改了代碼,以這樣的:

public sealed class QueuedLock { 
    private object innerLock = new object(); 
    private volatile int ticketsCount = 0; 
    private volatile int ticketToRide = 1; 
    ThreadLocal<int> reenter = new ThreadLocal<int>(); 

    public void Enter() { 
     reenter.Value++; 
     if (reenter.Value > 1) 
      return; 
     int myTicket = Interlocked.Increment(ref ticketsCount); 
     Monitor.Enter(innerLock); 
     while (true) { 
      if (myTicket == ticketToRide) { 
       return; 
      } else { 
       Monitor.Wait(innerLock); 
      } 
     } 
    } 

    public void Exit() { 
     if (reenter.Value > 0) 
      reenter.Value--; 
     if (reenter.Value > 0) 
      return; 
     Interlocked.Increment(ref ticketToRide); 
     Monitor.PulseAll(innerLock); 
     Monitor.Exit(innerLock); 
    } 
} 
相關問題