2011-05-13 58 views
1

我需要一個定時器等價物,它將定期執行一些特定的動作(例如更新數據庫中的某些進度或檢查要在數據庫中執行的新作業)。使用等待句柄的條件性定期計時器

這些操作綁定到WaitHandle,它指定是否需要執行作業。所以基本上這可以是,例如,當數據庫中存在新實體並且觸發搜索這些新實體時從外部設置的AutoResetEvent。計時器是必要的,因爲我想限制查詢數量到數據庫。因此,如果有10個新通知到達,AutomaticResetEvent將只設置一次,並且對於所有這些通知也只有一個查詢。

我的第一次嘗試是這樣的:

class ConditionalScheduler 
{ 
    public void AddConditionalAction(WaitHandle handle, Action action) 
    { 
     lock (syncRoot) 
     { 
      handles.Add(handle); 
      actions.Add(handle, action); 
     } 
    } 

    private readonly object syncRoot = new object(); 

    private readonly Dictionary<WaitHandle, Action> actions = new Dictionary<WaitHandle, Action>(); 
    private readonly List<WaitHandle> handles = new List<WaitHandle>(); 

    public void RunTimerLoop() 
    { 
     do 
     { 
      WaitHandle[] waitHandles = handles.ToArray(); 
      int index = WaitHandle.WaitAny(waitHandles); 

      if (index >= 0) 
      { 
       WaitHandle handle = waitHandles[index]; 

       Action action; 
       lock (syncRoot) 
       { 
        action = actions[handle]; 
       } 

       action(); 
      } 
      Thread.Sleep(5000); 
     } while (true); 
    } 

這種方法的問題是,WaitHandle.WaitAny只會給我說是引發了第一次的WaitHandle的指數。所以如果我有兩個或更多的WaitHandles被觸發,那麼我將只執行第一個動作而不是其他的動作。

您是否有更好的設計來達到執行在指定時間段內觸發的所有操作所需的結果?如果它簡化了這個問題,我可以使用另一種通知機制而不是WaitHandles。

謝謝!

回答

1

也許你可以去一個雙線程隊列/批量執行? 請注意,代碼是隻是一個概念,我正在寫沒有Visual Studio,所以我正在做一個列表,這最好做一個隊列,但我不記得沒有intellisense的語法:) ...

private readonly Dictionary<WaitHandle, Action> actions = new Dictionary<WaitHandle, Action>(); 
    private readonly List<WaitHandle> handles = new List<WaitHandle>(); 
    private readonly List<WaitHandle> triggeredHandles = new List<WaitHandle>(); 

// thread 1 
    public void CheckLoop() 
    { 
     do 
     { 
      WaitHandle[] waitHandles = handles.ToArray(); 
      int index = WaitHandle.WaitAny(waitHandles); 

      if (index >= 0) 
      { 
       lock (triggeredHandles) 
       { 
        triggeredHandles.Add(waitHandles[index]); 
       } 

      }    
     } while (true); 
    } 

// thread 2 
public void RunLoop() 
    { 
     do 
     { 
      lock(triggeredHandles) 
      { 
       foreach (var th in triggeredHandles) { 

        Action action; 
        lock (syncRoot) 
        { 
         action = actions[th]; 
        } 

        action();     
       } 
       triggeredHandles.Clear(); 
      } 
      Thread.Sleep(5000); 
     } while (true); 
    } 
+0

這看起來像會起作用。但我希望只有一個線程的解決方案......對我來說,使用2個線程似乎有點沉重。 – aKzenT 2011-05-13 09:11:35

+0

打開窗口任務管理器並轉到性能選項卡,查看您的操作系統上有多少線程正在運行。我現在有832個。所以我認爲一個mroe不會那麼糟糕:D – 2011-05-13 09:15:36

+0

和btw可以在一個線程中完成:如果將句柄添加到隊列中,然後不等待5000,如果有多於X個句柄要執行,則全部執行它們並然後等待5000 – 2011-05-13 09:18:31

0

通過使用Reactive Extensions (Rx),您可能會爲自己節省很多麻煩 - 它有很多偉大的方式來按照您要求的方式編寫事件。

This site是一種很方便的方法來查看rx可以做的事情。

1

您是否考慮過使用內建的Timer類? System.Threading.Timer是一個輕量級實現,它使用ThreadPoolSystem.Timers.Timer,它提供相關事件和更多功能。

+0

這並不能真正解決我的問題。我知道如何定期執行操作。我不知道的是如何引入條件行爲...... – aKzenT 2011-05-13 09:09:21

+0

@aKzenT:當然,但仍值得注意的是'Thread.Sleep'存在一些問題:從定時的角度來看它不準確,它保留一個線程,但不會讓它做任何工作,並且不能被打斷。 – 2011-05-13 09:33:00

+0

+1,你是正確的,但在我的情況下,準確性並不是那麼重要......我不喜歡這些計時器類,他們運行在ThreadPool上。我希望「蜱蟲」始終處於同一線程。不知道如何使用計時器類來解決這個問題。 – aKzenT 2011-05-13 10:48:26