【问题标题】:Wake up a thread when event occurs事件发生时唤醒线程
【发布时间】:2010-11-15 19:29:11
【问题描述】:

我想实现两个线程之间的如下通信:

Thread Alpha 做了一些事情,然后自己挂起。接下来,第二个线程(Beta)引发和恢复 Alpha 线程的事件。循环继续……

我做了类似下面的事情,但我不确定它是否是一个合适的设计。我还注意到 Thread.Suspend()Thread.Resume() 已被弃用。我期待听到有关此实现的任何建议以及替换已弃用方法的首选方法。

namespace ThreadTester
{
    delegate void ActionHandler();

    class Alpha
    {
        internal Thread alphaThread;
        internal void Start()
        {
            while (true)
            {
                this.alphaThread.Suspend();
                Console.WriteLine("Alpha");
            }
        }
        internal void Resume()
        {
            while (this.alphaThread.ThreadState == ThreadState.Suspended)
            this.alphaThread.Resume();
        }
    }

    class Beta
    {
        internal event ActionHandler OnEvent;
        internal void Start()
        {
            for (int i = 0; i < 15; i++)
            {
                OnEvent();
                Thread.Sleep(1000);
            }
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Alpha alpha = new Alpha();
            alpha.alphaThread = new Thread(new ThreadStart(alpha.Start));
            alpha.alphaThread.Start();
            while (!alpha.alphaThread.IsAlive) ;

            Beta beta = new Beta();
            beta.OnEvent += new ActionHandler(alpha.Resume);
            Thread betaThread = new Thread(new ThreadStart(beta.Start));
            betaThread.Start();
        }
    }
}

【问题讨论】:

    标签: c# multithreading events


    【解决方案1】:

    这是您通常使用wait handles 的地方,尤其是事件等待句柄。

    如果您在等待句柄上调用 WaitOne 方法,它将阻塞您的线程,直到其他线程在同一等待句柄上调用 Set

    有两种重要的简单形式的事件等待句柄:AutoResetEvent 将在线程通过WaitOne 后自动重置。 ManualResetEvent 只会在您调用 Reset 时自行重置。

    【讨论】:

      【解决方案2】:

      这是一个常见的同步问题,有多种方法(如果您不是特别小心,所有这些方法都很容易搞砸):

      1. 等待句柄(Joren 已经描述了这些)。

      2. Monitor.WaitMonitor.Pulse。这里的一个问题是 Pulse 只会唤醒已经处于等待状态的线程,因此您必须小心如何管理同步对象的锁定。

      3. 使用 .NET 4 中的新 TPL(也向后移植到 .NET 3.5),您可以设置异步任务并根据之前完成的任务定义任务继续执行的条件。了解如何构建代码以利用任务和延续有一点学习曲线,但它(从长远来看)比使用低级同步结构看似简单的非常崎岖的道路要好得多.这也为您提供了一条极好的前进道路,可以为您的逻辑添加更强大的错误处理和取消支持,因为协调这些的细节已融入 TPL。

      【讨论】:

        【解决方案3】:

        您的代码具有生产者-消费者模式的“感觉”,但以错误的方式实现。您绝对不想以这种方式(或任何实际方式)使用Thread.SuspendThread.Resume。通过BlockingCollection 类实现规范的生产者-消费者模式,实际上很容易以您想要的方式获得排序和信号发送。

        public class ProducerConsumer
        {
            private BlockingCollection<object> m_Queue = new BlockingCollection<object>();
        
            public ProducerConsumer()
            {
                new Thread(Producer).Start();
                new Thread(Consumer).Start();
            }
        
            private void Consumer()
            {
                while (true)
                {
                    object item = m_Queue.Take(); // blocks when the queue is empty
                    Console.WriteLine("Consumer");
                }
            }
        
            private void Producer()
            {
                while (true)
                {
                    m_Queue.Add(new object());
                    Thread.Sleep(1000);
                }
            }
        }
        

        在上面的代码中,ConsumerProducer 将分别等同于您的 alphaThreadbetaThread

        【讨论】:

        • 这可能最接近回答问题的真正意图,尽管很难确定。与直接使用低级同步构造相比,新的 BlockingCollection 绝对是实现这种简单的生产者-消费者模式的更好方法。也就是说,我建议 OP 谨慎使用这种模式,因为我已经看到它失控了(我已经看到至少一个应用程序有几十个线程都在运行他们自己的生产者-消费者“消息”处理程序[与该模式的三个完全不同的本土实现,不少于]。)
        【解决方案4】:

        通常使用线程来允许并行处理 > 1 项工作。我很好奇为什么你的设计需要线程 A 休眠而线程 B 做某事,然后醒来并继续工作。为什么不让线程 A 自己做呢?

        您可能会从使用 .Net 4 的 Task Parallel Library 中受益 - 然后线程 A 可以启动一个异步任务,该任务在单独的线程上自动执行,并且线程 A 可以使用结果而不需要显式的线程间信号(如果线程 A 或 B 发生故障,这可能会导致应用程序挂起)。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2010-10-29
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多