C# сигнализация двух потоков с AutoResetEvent


Мне нужно создать программу, которая рассчитывает до 10, используя два потока.
одна нить должна печатать четные числа, а другая должна печатать нечетные числа.
Эти темы должны печатать номера в порядке (1, 2, 3, 4, 5...)

Я сделал этот код, но, похоже, не работает... Есть идеи?

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApplication1
{
    class Program
    {
        private static AutoResetEvent autoEvent;
        private static int num = 10;

        static void DoWork(object data)
        {
            var counter = (int)data;

            if (counter == 1)
            {
                autoEvent.WaitOne();
            }

            while (counter <= num)
            {
                Console.WriteLine(counter);
                counter += 2;
                autoEvent.Set();
                autoEvent.WaitOne();
            }
        }

        static void Main(string[] args)
        {
            autoEvent = new AutoResetEvent(false);

            var evenCounter = new Thread(DoWork);
            evenCounter.Start(0);

            var notEvenCounter = new Thread(DoWork);
            notEvenCounter.Start(1);

            Console.ReadLine();
        }
    }
}

3 ответа

Решение

В этом коде autoEvent.WaitOne() может не блокировать, потому что AutoResetEvent уже в сигнальном состоянии:

        autoEvent.Set();
        autoEvent.WaitOne();

Так что поток не будет ждать другого потока, как вы запланировали.
Для решения этой проблемы вам нужно использовать 2 AutoResetEvents. Один поток будет ждать первого AutoResetEvent, второй поток будет ждать на втором AutoResetEvent, Каждый поток будет сигнализировать AutoResetEvent это "принадлежит" другому потоку.
Чтобы продемонстрировать мою идею, я покажу вам строки кода только там, где вам нужно внести изменения:

private static AutoResetEvent autoEvents[] = new AutoResetEvent[2];

...

autoEvents[0] = new AutoResetEvent(false);
autoEvents[1] = new AutoResetEvent(false);

...

var counter = (int)data;
int initial_counter = counter;

...

if (counter == 1)
{
autoEvents[initial_counter].WaitOne();
}

...

autoEvents[1 - initial_counter].Set();
autoEvents[initial_counter].WaitOne();

В конце DoWork подайте соответствующий сигнал AutoResetEvent чтобы избежать бесконечной блокировки другого потока:

autoEvents[1 - initial_counter].Set();

Здесь, кажется, ваша проблема:

   autoEvent.Set();
   autoEvent.WaitOne();

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

Я предлагаю вам использовать 2 блокировки, поэтому один поток может заблокировать другой.

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace ThreadTest
{
    class Program
    {
        static void Main(string[] args)
        {

            Thread t1 = new Thread(Method1);
            Thread t2 = new Thread(Method2);
            t1.Start();
            Thread.Sleep(100);
            t2.Start();
            t1.Join();
            t2.Join();
            _autoEvent1.Close();
            _autoEvent2.Close();
        }

        private static void Method1()
        {

            do
            {
               _autoEvent2.WaitOne();

                if (!SharedCode(Thread.CurrentThread.ManagedThreadId))
                {
                    break;
                }

                _autoEvent1.Set();
            } while (true);
        }

        private static void Method2()
        {
            do
            {
                _autoEvent1.WaitOne();
                //_autoEvent2.Set();
                if (!SharedCode(Thread.CurrentThread.ManagedThreadId))
                    break;
                _autoEvent2.Set();
            } while (true);
        }

        private static bool SharedCode(int threadId)
        {
           lock (_lockObject)
            {

                Interlocked.Increment(ref _count);
                if (_count > 10)
                    return false;
                Console.WriteLine("ThreadId={0} , count={1}", threadId,_count);
            }

            return true;
        }
        private  static AutoResetEvent _autoEvent1 = new AutoResetEvent(true);
        private static AutoResetEvent _autoEvent2 = new AutoResetEvent(true);
        private static Object _lockObject = new object();
        private static int _count = 0;
    }
}
Другие вопросы по тегам