2015-08-15 76 views
2

處理我有2類:暫停事件與C#

public class A 
{ 
    private const int MAXCOUNTER = 100500; 
    private Thread m_thrd; 
    public event Action<string> ItemStarted; 
    public event Action<string> ItemFinished; 

    private void OnItemStarted(string name) 
    { 
     if (ItemStarted != null) ItemStarted(name); 
    } 

    private void OnItemFinished(string name) 
    { 
     if (ItemFinished != null) ItemFinished(name); 
    } 

    public A() 
    { 
     m_thrd = new Thread(this.Run); 
     m_thrd.Start(); 
    } 

    private void Run() 
    { 
     for (int i = 0; i < MAXCOUNTER; i++) 
     { 
      OnItemStarted(i.ToString()); 
      // some long term operations 
      OnItemFinished(i.ToString()); 
     } 
    } 
} 

public class B 
{ 
    private Thread m_thrd; 
    private Queue<string> m_data; 
    public B() 
    { 
     m_thrd = new Thread(this.ProcessData); 
     m_thrd.Start(); 
    } 

    public void ItemStartedHandler(string str) 
    { 
     m_data.Enqueue(str); 
    } 

    public void ItemFinishedHandler(string str) 
    { 
     if (m_data.Dequeue() != str) 
      throw new Exception("dequeued element is not the same as finish one!"); 
    } 

    private void ProcessData() 
    { 
     lock (m_data) 
     { 
      while (m_data.Count != 0) 
      { 
       var item = m_data.Peek(); 
       //make some long term operations on the item 
      } 
     } 
    } 
} 

我們也有其他地方在代碼

A a = new A(); 
B b = new B(); 
a.ItemStarted += b.ItemStartedHandler; 
a.ItemFinished += b.ItemFinishedHandler; 
  • 所以,如果ItemFinished升高而ProcessData()仍在工作,會出現什麼發生?
  • 我是否應該使用類似AutoResetEvent的類A等級B完成ProcessData
  • 是否需要在ProcessData中使用lock
  • 可以調用B類的線程與m_thrd = new Thread(this.ProcessData);嗎?在這裏讓我感到困惑 - 在ItemStarted事件發生之前不會完成ProcessData(當B中的線程已經在第一次生成ItemStarted時已經完成),是不是會導致這種情況?

回答

1

你的代碼有點麻煩。

如果ItemFinishedProcessData()完成之前被提出,則您的第一個問題是競爭條件。

不用擔心使用AutoResetEvent。簡單的事情是鎖定每個訪問m_data。因此,導致下一個排隊 - 是的,鎖是必要的,並且在任何地方都是必要的。

但是你的最後一點是最重要的。您需要將每個構造函數更改爲.Start()方法,以便在開始之前有時間進行佈線。

但是,即使如此,你也有一個巨大的競爭條件。

以下是您應該做的工作。 NuGet「Rx-Main」將微軟的Reactive Framework添加到您的代碼中。然後執行此操作:

var scheduler1 = new EventLoopScheduler(); 
var scheduler2 = new EventLoopScheduler(); 

Observable 
    .Range(0, 100500) 
    .ObserveOn(scheduler1) 
    .Do(x => { /* some long term operations */ }) 
    .ObserveOn(scheduler2) 
    .Subscribe(x => 
    { 
     //make some long term operations on the item 
    }); 

工作完成。

+0

感謝您的回覆。但我必須在這裏明確地使用線程。 –

+0

這確實使用線程。事件循環調度程序每個使用單個線程進行計劃。 – Enigmativity

0

您需要更正您的代碼幾個線程問題:

  1. 您正在啓動線程後連接最多的事件。你應該在開始線程之前做到這一點。因此,對於這兩個類,將公共實例函數中的構造函數中的任何內容都放入。現在創建你的類的實例,連接事件,然後調用B類的實例方法,然後調用A類來啓動線程。
  2. 您正在從兩個不同的線程訪問B類中的隊列。隊列不是線程安全的,所以應該使用ConcurrentQueue(https://msdn.microsoft.com/en-us/library/dd267265(v=vs.110).aspx)和相應的Try ... Functions。您不需要使用ConcurrentQueue進行任何形式的顯式鎖定。
  3. 您對使用B類ProcessData函數的意圖不是很清楚。您是否想爲隊列中的每個項目調用一次ProcessData,或者是否希望繼續調用ProcessData,直到項目未出隊?如果,前者是這種情況,那麼應該使用BlockingCollection和GetConsumingEnumerable來代替Queue。詳情請參閱:https://msdn.microsoft.com/en-us/library/dd287186(v=vs.110).aspx。如果這是您想要的,我可以發佈樣本。如果您使用BlockingCollection,則不會出現ProcessData先於A類線程完成的情況。
+0

我必須在這裏具體說明:我有一個類選擇有關目錄和文件的信息(遞歸遍歷整個樹)和2個類來存儲數據在文件中並顯示在UI中。一般的想法是將文件夾名稱放在一個堆棧中(它在cl.A的同一線程中處理)和隊列中的文件信息(cl.B)。當dir中的所有文件都在隊列A中時,會掛起,直到B將它們處理到單獨的線程中。我使用CountdownEvent(因爲有2個額外的線程 - 所以要說B的實例)和Thread.Join()由於最初的需求同步的目的 - 不使用TPL,併發科爾等。 –