2011-05-06 43 views
1

我有一个正在运行的服务不断处理数据,它接收通过消息传递来处理新数据的请求。在忙于处理新的请求时,将它们合并在一起,以便一次处理它们。 AutoResetEvent用于通知处理器有新的请求可用。使用AutoResetEvent发送工作线程信号

我的问题是在EventLoop中,是否有可能在WaitOne之后的currentRequest为null?

在lock(_eventLocker)之外是否存在_eventAvailable.Set()方法是不好的做法?我把它移出来,这样它就不会在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)可以评估为true。考虑两个线程比赛呼吁_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. 
    } 
    } 
} 
+0

谢谢,我看着生产者的消费者,但由于我可以合并多个请求到一个单一的请求,我想要一个更手动的方法。在我的服务中,处理可能需要30秒,并且请求可能会每秒多次出现。我想我可以循环并使用TryTake将它们合并到消费者一方?那么会有一个m_Queue.Take()调用后面跟着一段时间(m_Queue.TryTake())吗? – BrandonAGr 2011-05-06 16:35:45

+0

@BrandonAGr:绝对。在调用'Take'获得初始请求后,您可以立即调用另一个调用'TryTake'的循环来快速选择几个请求。你在这里有很多可能性。但是,当队列为空时,您仍然需要调用'Take'来产生阻塞效应。 – 2011-05-06 16:41:00