2015-08-15 79 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,并发科尔等。 –