Dispatcher.Invoke在Windows服务中的异步读取期间“挂起”

时间:2011-09-15 14:50:42

标签: c# multithreading asynchronous dispatcher

我已经基于ServiceBase类创建了一个Windows服务。在这个服务中,我创建了一个NamedPipeClientStream(m_Stream)的实例。连接此流后,我使用BeginRead()方法启动异步读取:

m_Stream.BeginRead( m_ReadBuffer, 0, 2, ReadAsyncCallback, m_ReadInfo );

在回调例程ReadAsyncCallback中,确实被调用了,我为流调用EndRead()(它给出了读取的字节数,在本例中为2)。接下来,我想通知原始线程已完成读取。为此,我使用Dispatcher.Invoke方法:

m_Dispatcher.Invoke( new ReadDelegate( this.OnRead ), bytesRead);

(m_Dispatcher是使用System.Windows.Threading.Dispatcher.CurrentDispatcher在原始线程中创建的。)

此时我希望在原始线程中调用OnRead方法,但事实并非如此。 Invoke()方法没有返回,似乎是“挂起”。

我希望有人可以帮助我。如果您需要更多信息,请告诉我,我会尽快给您。

问候, 理查德

2 个答案:

答案 0 :(得分:3)

System.Windows.Threading.Dispatcher需要正确配置SynchronizationContext才能使其正常工作。在WPF应用程序的上下文中,会自动为您创建同步上下文,但是在Windows服务中没有发生这种情况,这就是您看到挂起的原因。

另外,除了同步上下文之外,由于我认为Dispatcher的工作方式与Windows窗体中的Control.InvokeBackgroundWorker类似,因此您的Windows服务主线程必须是消息循环,以便您能够将呼叫注入其中。

我写了一篇关于BackgroundWorker类如何根据其运行的上下文(Windows窗体,控制台或Windows服务)做出不同反应的博客,自从使用该机制以来,您可能会发现这是一个有趣的读取该类类似于WPF Dispatcher

Inside BackgroundWorker

最后,为了更深入地了解同步上下文如何工作,您应该阅读:

It's All About the SynchronizationContext

答案 1 :(得分:2)

调用CurrentDispatcher的线程可能由于某种原因没有提取消息。最可能的原因是因为它没有任何消息泵送机制。要使Invoke正常工作,必须专门设计目标线程以接受委托注入。这通常通过让目标线程在无限循环中旋转来等待消息出现在队列中来实现。然后,另一个线程将提交一条特殊消息,请求执行委托。这些都是在Windows窗体或WPF应用程序的UI线程上自动设置的。除非您手动完成,否则它将不存在于Windows服务应用程序中。

无论如何,我不会尝试使用这种委托编组技术(或任何将委托同步注入另一个线程的技术)。原因是它会导致在ThreadPool线程或IO完成端口线程上执行的异步IO回调阻塞,直到该编组委托完成为止。您不希望以这种方式绑定IO。

相反,您应该将从流中读取的数据发布到共享数据结构(如队列或列表)中,然后让原始线程在特定时间间隔内进行拾取。如果期望原始线程等待从流中读取数据,那么您可以设置生产者 - 消费者模式。使用BlockingCollection非常简单。原始线程将调用Take,它将阻塞直到项目到达,IO回调将通过调用Add发布数据。

还有其他可接受的方式可以处理,但调用Invoke可能不是其中之一。