Разбудить поток, когда происходит событие - PullRequest
6 голосов
/ 15 ноября 2010

Я хотел бы добиться следующей связи между двумя потоками:

Тема Альфа что-то делает, а затем приостанавливает себя. Далее возникает второй поток (бета) и событие, которое возобновляет поток альфа. Цикл продолжается ...

Я сделал что-то вроде ниже, но я не уверен, что это правильный дизайн. Также я заметил, что 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();
        }
    }
}

Ответы [ 4 ]

12 голосов
/ 15 ноября 2010

Здесь вы обычно используете дескрипторы ожидания , в частности дескрипторы ожидания события.

Если вы вызовете метод WaitOne для дескриптора ожидания, он заблокирует ваш поток до некоторогодругой поток вызывает Set с тем же дескриптором ожидания.

Существует два важных простых варианта дескрипторов ожидания события: AutoResetEvent автоматически сбросится после того, как поток пройдет через WaitOne. ManualResetEvent будет сбрасываться только при вызове Reset.

10 голосов
/ 15 ноября 2010

Это распространенная проблема синхронизации, и существует несколько подходов (каждый из которых довольно легко испортить, если вы не очень осторожны):

  1. Ручки ожидания (Джорен уже описал их).

  2. Monitor.Wait и Monitor.Pulse . Здесь есть одна проблема: Pulse будет пробуждать только те потоки, которые уже находятся в состоянии ожидания, поэтому вы должны быть осторожны с управлением блокировкой объекта синхронизации.

  3. С новым TPL в .NET 4 (также с портированием на .NET 3.5) вы можете устанавливать асинхронные задачи и определять условия, в которых задачи продолжаются на основе ранее выполненных задач. Существует некоторая кривая обучения для понимания того, как структурировать ваш код, чтобы использовать в своих интересах Задачи и продолжение, но это значительно лучше (в долгосрочной перспективе), чем очень трудный путь, который следует за обманчиво простым использованием конструкций синхронизации более низкого уровня , Это также дает вам отличный путь вперед для добавления более надежной обработки ошибок и поддержки отмены к вашей логике, так как мельчайшие детали их координации запечены в TPL.

7 голосов
/ 15 ноября 2010

Ваш код имеет «чувство» шаблона «производитель-потребитель», но реализован неправильно. Вы определенно не хотите использовать Thread.Suspend и Thread.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);
        }
    }
}

В приведенном выше коде Consumer и Producer будет эквивалентно вашим alphaThread и betaThread соответственно.

2 голосов
/ 15 ноября 2010

Обычно потоки используются для параллельной обработки> 1 элемента работы.Мне любопытно, почему ваш дизайн требует, чтобы поток A спал, в то время как поток B что-то делает, затем просыпается и продолжает работать.Почему бы просто не сделать так, чтобы поток A работал сам по себе?

Возможно, вы выиграете от использования параллельной библиотеки задач .Net 4 - тогда поток A может инициировать асинхронную задачу, которая автоматически выполняется в отдельнойпоток, и результат стал доступным для потока A без необходимости явной сигнализации между потоками (что может привести к зависанию приложения в случае неисправности потока A или B).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...