2011-02-22 53 views
0

我不知道如何確保一個線程正在等待事件。 讓說我是引發事件的組件:如何確保線程正在等待事件?

public delegate void TestHandler(object sender, EventArgs eventArgs); 
class Producer 
{ 
    public event TestHandler Handler; 
    public void InvokeHandler(EventArgs eventargs) 
    { 
     var handler = Handler; 
     if (handler != null) handler(this, eventargs); 
    } 

    public Producer() 
    { 
     Task.Factory.StartNew(() => 
     { 
      while (true) 
      { 
       Thread.Sleep((new Random()).Next(0,100)); 
       InvokeHandler(new EventArgs()); 
      } }); } } 

與聽衆:

class Listener 
{ 
    private readonly BlockingCollection<EventArgs> _blockingCollection; 
    public Listener() 
    { 
     var producer = new Producer(); 
     _blockingCollection = new BlockingCollection<EventArgs>(10); 
     producer.Handler += producer_Handler; 
    } 

    void producer_Handler(object sender, EventArgs eventArgs) 
    { 
     _blockingCollection.TryAdd(eventArgs); //no error handling for simplicity sake 
    } 

    internal void ProcessEvents() 
    { 
     while (true) 
     { 
      EventArgs eventArgs; 
      try 
      { 
       if (_blockingCollection.TryTake(out eventArgs)) 
        Console.WriteLine("Received event"); 
      } 
      catch (Exception ex) 
      { 
       Console.WriteLine(ex.Message); 
      } } } } 

如果我將開始它:

class Program 
    { 
     static void Main(string[] args) 
     { 
      var listner = new Listener(); 
      listner.ProcessEvents(); 
     } } 

我得到預期的行爲(從我時不時收到事件通過阻止收集。

但是,如果我會w在任務中說唱這個電話:

Task.Factory.StartNew(() => { 
    var listner = new Listener(); 
    listner.ProcessEvents(); }); 

我永遠不會進入處理部分。 任何想法爲什麼會這樣?我錯過了明顯的東西嗎?

一邊,有人知道一個模式,這將有助於這裏很好的描述? 我正在尋找在一個線程(最好不是主應用程序線程)上拾取事件,然後在一個單獨的線程上同時處理它們,如果事件數量變得太高(典型的閾值我假設)

在此先感謝,

+1

當你說你在開始任務時從未到達處理部分,發生了什麼?該程序是否簡單退出? – 2011-02-22 23:15:39

+0

#paul,@stic,就是我想問的。錯誤消息和堆棧轉儲請。我們可以看看這個測試設置的一個神器。 – 2011-02-22 23:20:03

+0

Paul,Henk - 會重複練習幾次,但它看起來像一個乾淨的運行,只是完成忽略到while(true)循環 – stic 2011-02-23 00:12:59

回答

0

爲了弄清楚發生了什麼事情,你應該把一些日誌信息,並確認時,當您啓動新的任務沒有異常拋出:

Thread listenerThread = new Thread(() => 
{ 
    // print a message when the task is started 
    Console.WriteLine("Task started!"); 

    var listner = new Listener(); 
    listner.ProcessEvents(); 
}); 

// set your thread to background so it doesn't live on 
// even after you're 
listenerThread.IsBackground = true; 
listenerThread.Start(); 

的Thread.join( )和Task.Wait()一樣。你可以把它稱爲不指定超時,你可以指定一個無限超時,或者如果你想你的測試停止等待的線程來完成,那麼你指定以毫秒爲單位:

// this will block until the thread is complete 
listenerThread.Join(Timeout.Infinite); 

如果您需要停止測試,那麼你必須執行一個異步中斷。中斷引發ThreadThreadInterruptedException這你應該趕上(如ProcessEvents方法所示):

listenerThread.Interrupt(); 

此外,您使用的是阻塞集合,但你不使用的攔截功能集合。沒有理由爲什麼你應該繼續重試時,有沒有集合中,而不是你寧願阻塞,直到有東西在它(這是這個系列的目的是做):

internal void ProcessEvents() 
{ 
    while (true) 
    { 
     try 
     { 
      var myEvent = _blockingCollection.Take(); 
      Console.WriteLine("Received event"); 
     } 
     catch(ThreadInterruptedException) 
     { 
      Console.WriteLine("Thread interrupted!"); 
      break;// break out of the loop! 
     } 
     catch (Exception ex) 
     { 
      Console.WriteLine(ex.Message); 
      // Do you REALLY want to keep going if there is an unexpected exception? 
      // Consider breaking out of the loop... 
     } 
    } 
} 
+0

謝謝Lirik,我不知道TryTake包裹在如果不會被阻塞。剛剛學習如何離開基於監視器的有界緩衝區後面... – stic 2011-02-23 10:10:10

+0

@stic [TryTake is non-blocking](http://msdn.microsoft.com/en-us/library/dd287184.aspx)期間,它doesn如果你用if語句或其他語句包裝它,無所謂。只有[Take is blocking](http://msdn.microsoft.com/en-us/library/dd287085.aspx)... – Kiril 2011-02-23 15:34:35

1

愚蠢的我。 ..

當我在任務中包裝我的呼叫時,它在後臺線程上運行。 我應該在退出前等待任務完成。

已經這樣做了,那就是分配:

var task = Task.Factory.StartNew(/*task body*/); 
task.Wait(); 

,等待我能得到我想要的東西。

+0

這樣做,但我仍然建議您使用阻止'Take'而不是非阻塞的'TryTake' ...此外,當你有一個消費者阻止/等待某些東西時,那麼你真的想在一個'Thread'而不是'Task'中運行它(給你阻斷呼叫的能力)。 – Kiril 2011-02-23 15:52:40

+0

我正在傳遞CancellationToken,同時開始一項新任務。我用try/catch封裝了一下,並處理了取消引發的異常。我試圖擺脫使用Thread類,會看到我能得到多遠:-) – stic 2011-02-28 11:41:33