Когда вы сигнализируете с помощью ManualResetEvent.Set
, вы гарантируете, что все потоки, которые ожидают этого события (т.е. в состоянии блокировки на flag.WaitOne
), будут сигнализироваться перед возвратом управления вызывающей стороне.
Конечно, есть обстоятельства, когда вы можете установить флаг, и ваш поток не увидит его, потому что он выполняет некоторую работу, прежде чем проверяет флаг (или, как подсказывает nobugs, если вы создаете несколько потоков):
ThreadPool.QueueUserWorkItem(s =>
{
QueryDB();
flag.WaitOne();
Console.WriteLine("Work Item 1 Executed");
});
Существует конфликт на флаге, и теперь вы можете создать неопределенное поведение при его закрытии. Ваш флаг является общим ресурсом между вашими потоками, вы должны создать защелку обратного отсчета, по которой каждый поток сигнализирует о завершении. Это устранит конфликт на вашем flag
.
public class CountdownLatch
{
private int m_remain;
private EventWaitHandle m_event;
public CountdownLatch(int count)
{
Reset(count);
}
public void Reset(int count)
{
if (count < 0)
throw new ArgumentOutOfRangeException();
m_remain = count;
m_event = new ManualResetEvent(false);
if (m_remain == 0)
{
m_event.Set();
}
}
public void Signal()
{
// The last thread to signal also sets the event.
if (Interlocked.Decrement(ref m_remain) == 0)
m_event.Set();
}
public void Wait()
{
m_event.WaitOne();
}
}
- Каждый поток сигнализирует о защелке обратного отсчета.
- Ваш основной поток ожидает защелки обратного отсчета.
- Главный поток очищается после сигналов защелки обратного отсчета.
В конечном итоге время, когда вы спите в конце, НЕ является безопасным способом решения ваших проблем, вместо этого вы должны разработать свою программу, чтобы она была на 100% безопасна в многопоточной среде.
ОБНОВЛЕНИЕ: один производитель / несколько потребителей
Предполагается, что ваш производитель знает, сколько потребителей будет создано, После создания всех ваших потребителей вы сбрасываете CountdownLatch
с указанным количеством потребителей:
// In the Producer
ManualResetEvent flag = new ManualResetEvent(false);
CountdownLatch countdown = new CountdownLatch(0);
int numConsumers = 0;
while(hasMoreWork)
{
Consumer consumer = new Consumer(coutndown, flag);
// Create a new thread with each consumer
numConsumers++;
}
countdown.Reset(numConsumers);
flag.Set();
countdown.Wait();// your producer waits for all of the consumers to finish
flag.Close();// cleanup