Использование AutoResetEvent для сигнализации рабочего потока
У меня есть служба, которая постоянно обрабатывает данные, она получает запросы на обработку новых данных через обмен сообщениями. Пока он занят обработкой новых запросов, они объединяются вместе, поэтому все они обрабатываются одновременно. AutoResetEvent используется для уведомления процессора о наличии нового запроса.
Мой вопрос в EventLoop, возможно ли, чтобы currentRequest после WaitOne был нулевым?
Является ли плохой практикой использование _eventAvailable.Set() вне блокировки (_eventLocker)? Я переместил это так, чтобы это не начало идти в WaitOne и немедленно оспорить блокировку (_eventLocker).
Любые предложения о том, как лучше написать следующий код?
public sealed class RealtimeRunner : MarshalByRefObject
{
/// <summary>
/// The actual event, new events get merged into this if it is not null
/// </summary>
private Request _pendingRequest;
/// <summary>
/// Used to signal the runner thread when an event is available to process
/// </summary>
private readonly AutoResetEvent _eventAvailable = new AutoResetEvent(false);
private readonly object _eventLocker = new object();
/// <summary>
/// Called on a background thread via messaging
/// </summary>
public void QueueEvent(RealtimeProcessorMessage newRequest)
{
bool mergedRequest;
lock (_eventLocker)
{
if (_pendingRequest == null)
{
mergedRequest = false;
_pendingRequest = new Request(newRequest, _engine);
}
else
{
mergedRequest = true;
_pendingRequest.Merge(newRequest, _engine);
}
}
_eventAvailable.Set();
}
/// <summary>
/// This is running on its own thread
/// </summary>
private void EventLoop()
{
while (true)
{
// Block until something exists in _pendingRequest
_eventAvailable.WaitOne();
Request currentRequest;
lock (_eventLocker)
{
currentRequest = _pendingRequest;
_pendingRequest = null;
}
// CAN THIS EVER BE NULL?
if (currentRequest == null)
continue;
//do stuff with the currentRequest here
}
}
}
1 ответ
Да, if (currrentRequest == null)
мог бы оценить, чтобы правда. Рассмотрим две темы гонки на вызов _eventAvailable.Set()
, Один завершает вызов, а другой прерывается. Между тем EventLoop
поток просыпается и завершает всю итерацию цикла. Теперь у вас есть ситуация, когда _pendingRequest
является нулевым и WaitHandle
все еще ждет, чтобы получить сигнал снова.
Я хочу представить совершенно другое решение проблемы. Похоже, ваш код может быть упрощен с помощью шаблона "производитель-потребитель". Этот шаблон легче всего реализовать с помощью очереди блокировки. BlockingCollection
класс реализует такую очередь.
public sealed class RealtimeRunner : MarshalByRefObject
{
private BlockingCollection<Request> m_Queue = new BlockingCollection<Request>();
public void QueueEvent(RealtimeProcessorMessage newRequest)
{
m_Queue.Add(new Request(newRequest _engine));
}
private void EventLoop()
{
while (true)
{
// This blocks until an item appears in the queue.
Request request = m_Queue.Take();
// Process the request here.
}
}
}