2

我有 2 节课:

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吗?
  • 有必要用lockProcessData吗?
  • 可以用 调用类B的线程m_thrd = new Thread(this.ProcessData);吗?这里的事情让我感到困惑 - 在引发任何事件之前不会ProcessData完成ItemStarted(它不会导致第一次生成线程时B已经完成的情况)?ItemStarted
4

2 回答 2

1

您的代码有点混乱。

ItemFinished如果在完成之前提出,您的第一个问题是竞争条件ProcessData()

不用担心使用AutoResetEvent. 简单的事情是锁定对m_data. 所以这就引出了下一个问题——是的,锁是必要的,而且在任何地方都是必要的。

但你的最后一点是最重要的。您需要将每个构造函数更改为一种.Start()方法,以便在开始之前有时间进行连接。

但是,即使那样,你也有一个巨大的竞争条件。

这是您应该做的事情。NuGet "Rx-Main" 将 Microsoft 的响应式框架添加到您的代码中。然后这样做:

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
    });

任务完成。

于 2015-08-15T08:44:27.723 回答
0

您需要更正代码中的几个线程问题:

  1. 您正在启动线程后连接事件。您应该在启动线程之前执行此操作。因此,对于这两个类,将构造函数中的任何内容放入 Public 实例函数中。现在创建类的实例,连接事件,然后首先调用 B 类的实例方法,然后调用 A 类来启动线程。
  2. 您正在从两个不同的线程访问 B 类中的队列。队列不是线程安全的,因此,您应该使用 ConcurrentQueue ( https://msdn.microsoft.com/en-us/library/dd267265(v=vs.110).aspx ) 和相应的 Try... 函数。您不需要使用 ConcurrentQueue 进行任何类型的显式锁定。
  3. 您使用 B 类 ProcessData 函数的意图不是很清楚。您想为队列中的每个项目调用一次 ProcessData 还是要继续调用 ProcessData 直到项目未出列?如果是前者,那么您应该使用 BlockingCollection 和 GetConsumingEnumerable,而不是使用 Queue。有关详细信息,请参阅:https ://msdn.microsoft.com/en-us/library/dd287186(v=vs.110).aspx 。如果这是你想要的,我可以发布一个样本。如果您使用 BlockingCollection,则不会出现 ProcessData 比 A 类线程更早完成的情况。
于 2015-08-17T02:19:10.483 回答