使用 AutoResetEvent 向工作线程发出信号

发布于 2024-11-05 16:15:05 字数 2041 浏览 3 评论 0原文

我有一个正在运行的不断处理数据的服务,它通过消息传递接收处理新数据的请求。当它忙于处理新请求时,它们会合并在一起,以便立即处理所有请求。 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

想你的星星会说话 2024-11-12 16:15:05

是的,if (currrentRequest == null) 的计算结果可能为 true。考虑两个线程竞相调用_eventAvailable.Set()。一个完成呼叫,另一个被抢占。同时,EventLoop 线程被唤醒并完成循环的整个迭代。现在,您遇到的情况是 _pendingRequest 为 null,并且 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.
    }
  }
}

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 the EventLoop thread wakes up and completes an entire iteration of the loop. You now have a situation where _pendingRequest is null and the WaitHandle 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.

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.
    }
  }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文