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 AutoResetEvent
s. Один поток будет ждать первого 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;
}
}