2009-09-02 46 views
3

我的问题是这样的:是否有可能使用ISynchronizeInvoke.BeginInvoke()重载一个线程?

我有两个线程,我的UI线程和一个工作线程。我的工作线程运行在一个由窗体实例化的独立类中,该窗体将其自身作为ISynchronizeInvoke传递给工作类,然后在该接口上使用Invoke来调用它的事件,这些事件向UI提供状态更新以供显示。这很奇妙。

我注意到我的后台线程似乎运行速度很慢,所以我将电话改为InvokeBeginInvoke,认为“我只是提供进度更新,它不需要完全同步,没有伤害完成“,除了现在我越来越奇怪的进度更新。我的进度条更新,但标签的文本没有,如果我改变到另一个窗口,并尝试返回,它的行为就像UI线程被锁定,所以我想知道如果我的进度调用(这发生很通常)会重载UI线程以至于它从不处理消息。这是可能的,还是有其他工作在这里?

回答

3

你明确地重载UI线程。

在第一个样品,你是(幕后)发送消息到UI线程,等待其进行处理(这是调用,最终依赖于SendMessage的目的),然后发送另一个。与此同时,其他消息可能已入队(例如WM_PAINT消息)并进行处理。

在您的第二个示例中,通过使用BeginInvoke(最终依赖于PostMessage),您在消息队列中大量排入消息,消息泵必须按顺序处理。当然,虽然它处理了成千上万条消息,但它无法处理让您的用户界面看起来“冻结”的操作系统消息(WM_PAINT等)。

您可能提供的状态更新太多;尽量降低反馈水平。

如果您想更好地了解消息在Windows中的工作方式,请从this开始。

1

有一些想法;

  • 尝试批量更新;例如,在循环中每迭代没有更新;取决于速度,可能每50/500。在列表的情况下,你可以在本地列表变量中缓冲,通过Invoke/BeginInvoke取得列表,并且在UI线程上处理缓冲区
  • 变量捕获;如果您使用的是BeginInvoke和匿名方法,您可能会遇到问题...我将在
  • 之下添加一个示例
  • 使UI更新有效 - 尤其是如果您正在处理列表;一些控件(特别是基于列表的控件)有一对像BeginEdit/EndEdit这样的方法,当您进行大量更新时停止UI重绘;相反,它等待,直到End*

捕获问题...想象(工人):

List<string> stuff = new List<string>(); 
for(int i = 0 ; i < 50000 ; i++) { 
    stuff.Add(i.ToString()); 
    if((i % 100) == 0) { 
     // update UI 
     BeginInvoke((MethodInvoker) delegate { 
      foreach(string s in stuff) { 
       listBox.Items.Add(s); 
      } 
     }); 
    } 
} 

你有没有注意到,在某些时候两个线程都说话stuff?UI线程可以迭代它,而工作线程(一直运行在BeginInvoke之后)继续添加。这可能会导致问题。通常不会性能问题(除非您捕捉异常并花费很长时间来记录它们),但肯定会有问题。此选项将包括:

  • 使用Invoke运行更新同步
  • 创建每次更新一个新的缓冲,使两个线程永远不会具有相同的列表实例(你需要在非常仔细地看虽然)
相关问题