我正在研究一个数据绑定很大的Win.Forms应用程序,我发现了一些奇怪的行为。该应用程序具有单独的I / O线程,通过异步Web请求接收更新 然后,它将其发送到主/ GUI线程,以便处理和更新应用程序范围的数据存储(进而可以将数据绑定到各种GUI元素等)。 Web请求另一端的服务器需要定期请求或会话超时。
我已经完成了几个处理线程问题的尝试解决方案,我发现了以下行为:
如果我使用Control.Invoke将更新从I / O线程发送到主线程,并且此更新导致显示MessageBox,主表单的消息泵将停止,直到用户单击ok-按钮。这也会阻止I / O线程继续最终导致服务器超时。
如果我使用Control.BeginInvoke将更新从I / O线程发送到主线程主表单的消息泵不停止,但是如果处理更新导致显示消息框,其余更新的处理将暂停,直到用户单击确定。由于I / O线程继续运行并且消息泵继续处理消息,因此可以在具有消息框的那个之前调用几个BeginInvoke用于更新。这会导致无序更新,这是不可接受的。
I / O线程向阻塞队列添加更新(非常类似于Creating a blocking Queue<T> in .NET?)。 GUI线程使用Forms.Timer定期应用阻塞队列中的所有更新。该解决方案解决了阻塞I / O线程和更新顺序性的问题,即下一次更新将永远不会开始,直到上一次完成。但是,性能成本较低,并且在显示更新方面存在延迟,从长远来看这是不可接受的。我希望主线程中的更新处理是事件驱动而不是轮询。
所以我的问题。我应该怎么做:
更新:请参阅下面的解决方案
答案 0 :(得分:5)
MessageBox本身泵出一个消息循环。那当然不是Windows Forms消息循环。一切都正常运行,但减去Control.BeginInvoke()发布的委托调用请求的调度。只有Windows窗体消息循环可以做到这一点。
当在UI线程上进行MessageBox.Show()调用时,会发生这种情况。但是,当它在工作线程上生成时,消息队列是每线程属性。如果你可以将Show调用委托给一个worker,你可能会解决你的问题。
解决您的问题:
你真的想要相反:工作线程应该阻止。不阻塞会导致重大问题,BeginInvoke调度队列将无限制地填满。一个可能的技巧是计算BeginInvoke调用的数量,在委托目标中倒计时。使用Interlocked类。
保证BeginInvoke目标的执行顺序。真正的问题可能与工作线程不同步有关。
在帖子上显示消息框。
答案 1 :(得分:1)
所以你有一个复杂的数据采集和处理链,你想继续运行,然后你在那里插入一个MessageBox。线程+调用中的任何内容都不会改变MessageBox是模态的事实,并且您必须等待它关闭,使整个链依赖于用户点击某些内容。
所以,至少在主路径中摆脱MessageBox。如果处理的一部分确实需要用户干预,则该段必须位于单独的线程上。
答案 2 :(得分:1)
不要使用Forms.Timer从队列中应用更新,而是使用另一个线程来执行此操作。该线程持续监视队列,并且(可能)告诉GUI何时使用新数据刷新自身(通过BeginInvoke)可以从此队列读取器线程显示MessageBox - 不必是GUI线程。
编辑:队列使用者可以调用Control.Invoke来显示messageBox以解决z-order问题
答案 3 :(得分:0)
以下是我最终解决的问题:
此解决方案与之前的解决方案相比具有以下优势(在上面的3.中使用了用于GUI更新的轮询):
更新:使用此解决方案显示消息框时,GUI更新似乎仍处于锁定状态。修复时会更新。
更新2:通过将Invoke更改为BeginInvoke来更新工作线程的修复。