Dispatcher.Invoke“挂起”在 Windows 服务中异步读取期间
我创建了一个基于 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 方法,但事实并非如此。 t。 Invoke() 方法没有返回,它似乎“挂起”。
我希望有人能帮助我解决这个问题。如果您需要更多信息,请告诉我,我会尽力尽快给您。
问候, 理查德
I've created a Windows service based on the ServiceBase class. In this service I created an instance of the NamedPipeClientStream (m_Stream). After connecting this stream I start an asynchronous read using the BeginRead() method:
m_Stream.BeginRead( m_ReadBuffer, 0, 2, ReadAsyncCallback, m_ReadInfo );
In the callback routine ReadAsyncCallback, which indeed gets called, I call EndRead() for the stream (which gives me the number of bytes read, in this case 2). Next, I want to signal the original thread that the read has been completed. For this I use the Dispatcher.Invoke method:
m_Dispatcher.Invoke( new ReadDelegate( this.OnRead ), bytesRead);
(m_Dispatcher was created in the original thread using System.Windows.Threading.Dispatcher.CurrentDispatcher.)
At this point I expected the OnRead method to get called in the original thread, but it doesn't. The Invoke() method doesn't return, it seems to 'hang'.
I hope someone can help me with this. Please let me know if you need more info, I will try to give it to you asap.
Greetings,
Richard
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
System.Windows.Threading.Dispatcher
需要正确配置的SynchronizationContext
才能使其按您通常期望的方式工作。当在 WPF 应用程序的上下文中时,会自动为您创建同步上下文,但是在 Windows 服务中不会发生这种情况,这就是您看到挂起的原因。另外,除了同步上下文之外,由于我相信
Dispatcher
的工作方式与 Windows 窗体中的Control.Invoke
或BackgroundWorker
类似,因此您的Windows 服务主线程必须泵送消息循环,以便您能够将调用注入其中。我写了一篇博客,介绍
BackgroundWorker
类如何根据其运行的上下文(Windows 窗体、控制台或 Windows 服务)做出不同的反应,您可能会发现这篇文章很有趣,因为所使用的机制该类类似于 WPFDispatcher
。BackgroundWorker 内部
最后,更深入地了解同步是如何进行的您应该阅读的上下文工作:
这都是关于同步上下文
The
System.Windows.Threading.Dispatcher
requires a correctly configuredSynchronizationContext
in order for it to work as you normally expect. When in the context of a WPF application the synchronization context is automatically created for you, however in your Windows Service that does not happen and that's why you see the hang.Also, aside the synchronization context, since I believe the
Dispatcher
works in a similar way to theControl.Invoke
orBackgroundWorker
in Windows Forms, your Windows Service main thread must be pumping a message loop in order for you to be able to inject your call into it.I have written a blog about how the
BackgroundWorker
class reacts differently according to the context in which its run (Windows Forms, Console or Windows Service), which you may find to be an interesting read since the mechanism used by that class is similar to the WPFDispatcher
.Inside BackgroundWorker
Finally, for a more in depth dive into how the synchronization contexts work you should read:
It's All About the SynchronizationContext
调用
CurrentDispatcher
的线程可能由于某种原因没有发送消息。最可能的原因是它没有任何消息泵机制。为了使 Invoke 正常工作,目标线程必须专门设计为接受委托注入。这通常是通过让目标线程在无限循环中旋转等待消息出现在队列中来完成的。然后另一个线程将提交一条特殊消息,请求执行委托。这一切都是在 Windows 窗体或 WPF 应用程序的 UI 线程上自动设置的。除非您手动启动它,否则它不会存在于 Windows 服务应用程序中。无论如何,我不会尝试使用此委托封送技术(或任何将委托同步注入另一个线程的技术)。原因是它会导致在 ThreadPool 线程或 IO 完成端口线程上执行的异步 IO 回调阻塞,直到封送委托完成。您不希望以这种方式束缚 IO。
相反,您应该将从流中读取的数据发布到共享数据结构中,例如队列或列表,然后让原始线程按特定时间间隔获取它。如果原始线程需要等待从流中读取数据,那么您可以设置生产者-消费者模式。使用
BlockingCollection
这非常容易。原始线程将调用Take
,该线程将阻塞,直到有项目到达,并且 IO 回调将通过调用Add
发布数据。还有其他可接受的方法可以处理此问题,但调用
Invoke
可能不是其中之一。The thread that called
CurrentDispatcher
is probably not pumping messages for some reason. The most likely reason is because it does not have any message pumping mechanism. ForInvoke
to work correctly the target thread must be specially designed to accept delegate injections. This is usually accomplished by having the target thread spin in an infinite loop waiting for messages to appear in a queue. Another thread would then submit a special message requesting the execution of a delegate. This is all setup automatically on the UI thread of Windows Forms or WPF applications. It will not exist in Windows Service application unless you get it going manually.I would not attempt to use this delegate marshaling technique (or any technique that synchronously injects a delegate into another thread) anyway. The reason is because it will cause that asynchronous IO callback, which is executing on a
ThreadPool
thread or IO completion port thread, to block until that marshaled delegate completes. You do not want to tie up the IO in this manner.Instead you should publish the data that is read from the stream into a shared data structure, like a queue or list, and then have your original thread pick it up on a specific interval. If the original thread is expected to wait for data to be read from the stream then you could setup the producer-consumer pattern. This is pretty easy with the
BlockingCollection
. The original thread will callTake
which will block until an item arrives and the IO callback will publish the data by callingAdd
.There are other acceptable ways this could be handled, but calling
Invoke
is probably not one of them.