使用 AutoResetEvent 向工作线程发出信号
我有一个正在运行的不断处理数据的服务,它通过消息传递接收处理新数据的请求。当它忙于处理新请求时,它们会合并在一起,以便立即处理所有请求。 AutoResetEvent 用于通知处理器有新请求可用。
我的问题是在EventLoop中WaitOne之后的currentRequest是否有可能为空?
将 _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
}
}
}
I have a service that is running constantly processing data, it receives requests to process new data through messaging. While it's busy processing new requests get merged together so that they are then all processed at once. An AutoResetEvent is used to notify the processor that a new request is available.
My question is in EventLoop should it be possible that currentRequest after the WaitOne to be null?
Is it bad practice to have the _eventAvailable.Set() outside of the lock(_eventLocker)? I moved it out so that it wouldn't start going at the WaitOne and immediately contest the lock(_eventLocker).
Any suggestions on how to better write the following code?
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
}
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
是的,
if (currrentRequest == null)
的计算结果可能为 true。考虑两个线程竞相调用_eventAvailable.Set()
。一个完成呼叫,另一个被抢占。同时,EventLoop
线程被唤醒并完成循环的整个迭代。现在,您遇到的情况是_pendingRequest
为 null,并且WaitHandle
仍在等待再次发出信号。我想提出一个完全不同的解决方案来解决这个问题。看来您的代码可以通过使用生产者-消费者模式来简化。使用阻塞队列最容易实现此模式。
BlockingCollection
类实现了这样的队列。Yes, the
if (currrentRequest == null)
could evaluate to true. Consider two threads racing to call_eventAvailable.Set()
. One completes the call and the other gets preempted. Meanwhile theEventLoop
thread wakes up and completes an entire iteration of the loop. You now have a situation where_pendingRequest
is null and theWaitHandle
is still waiting to be signaled again.I want to present an entirely different solution to the problem. It looks your code could be simplified by using the producer-consumer pattern. This pattern is most easily implemented using a blocking queue. The
BlockingCollection
class implements such a queue.