2008-09-24 54 views
1

我的表单从随机工作线程上的另一个对象接收异步回调。我一直在使用代理将数据传递给主线程(可用于更新屏幕控件),如下所示。性能是可怕的 - 一旦我达到每秒500次更新,程序完全锁定。我的GUI处理本身不是问题,因为我可以在窗体中模拟这种级别的更新并且没有问题。我应该使用更有效的机制将数据从线程切换到线程吗?Windows窗体线程和事件 - 切换事件的最有效方式?

delegate void DStatus(MyStatus obj); 
DStatus _status; // set to MainThreadOnStatus during construction 

// this function only called on form's owner thread 
void MainThreadOnStatus(MyStatus obj) 
{ 
    // screen updates here as needed 
} 

// this function called by arbitrary worker threads in external facility 
void OnStatus(MyStatus obj) 
{ 
    this.BeginInvoke(_status, obj); 
} 

回答

1

您可能不需要更新每个事件的UI,而是“不如每秒X次”更新。您可以利用StopWatch或其他计时系统在一段时间内收集事件,然后在适当时更新UI。

如果您需要捕获所有事件,请每隔一段时间将它们收集到Queue和Fire事件中,并且该事件处理程序将为所有排队事件处理Queue并更新UI。

0

这是很难说确切的问题,但一些可能性...

是您MyStatus对象,要传递到的onStatus从MarshalByRefObject(和它的每一个对象)衍生?如果不是这样,它会在每个打包的电话上进行分组,并可能导致巨大的性能损失。

此外,在使用控件调用委托之前,您应该确实调用this.InvokeRequired,但实际上这只是最佳做法。

+0

MarshalByRefObject对单个appdomain中的线程边界有影响吗?我认为它只适用于你有多个应用程序域。 – Eric 2008-09-24 23:15:10

1

我一直在做什么伊利亚一直暗示。对于不需要“实时”响应的用户界面,我有一个每秒钟跳两次的秒表。对于更快的更新,我使用一个队列或其他数据结构来存储事件数据,然后使用“lock(queue){}”来避免争用。如果你不想减慢工作线程,你必须确保UI线程不会阻塞工作者太久。

2

我不是定时器的忠实粉丝,如果你想要一个更加事件驱动的方法,尝试是这样的:

public class Foo 
{ 
    private AsyncOperation _asyncOperation = null; 
    private SendOrPostCallback _notifyNewItem = null; 

    //Make sure you call this on your UI thread. 
    //Alternatively you can call something like the AttachUI() below later on and catch-up with 
    //your workers later. 
    public Foo() 
    { 
     this._notifyNewItem = new SendOrPostCallback(this.NewDataInTempList); 
     this._asyncOperation = AsyncOperationManager.CreateOperation(this); 
    } 

    public void AttachUI() 
    { 
     if (this._asyncOperation != null) 
     { 
      this._asyncOperation.OperationCompleted(); 
      this._asyncOperation = null; 
     } 

     this._asyncOperation = AsyncOperationManager.CreateOperation(this); 
     //This is for catching up with the workers if they’ve been busy already 
     if (this._asyncOperation != null) 
     { 
      this._asyncOperation.Post(this._notifyNewItem, null); 
     } 
    } 


    private int _tempCapacity = 500; 
    private object _tempListLock = new object(); 
    private List<MyStatus> _tempList = null; 

    //This gets called on the worker threads.. 
    //Keeps adding to the same list until UI grabs it, then create a new one. 
    public void Add(MyStatus status) 
    { 
     bool notify = false; 
     lock (_tempListLock) 
     { 
      if (this._tempList == null) 
      { 
       this._tempList = new List<MyStatus>(this._tempCapacity); 
       notify = true; 
      } 

      this._tempList.Add(status); 
     } 
     if (notify) 
     { 
      if (this._asyncOperation != null) 
      { 
       this._asyncOperation.Post(this._notifyNewItem, null); 
      } 
     } 
    } 

    //This gets called on your UI thread. 
    private void NewDataInTempList(object o) 
    { 
     List<MyStatus> statusList = null; 
     lock (this._tempListLock) 
     { 
      //Grab the list, and release the lock as soon as possible. 
      statusList = this._tempList; 
      this._tempList = null; 
     } 
     if (statusList != null) 
     { 
      //Deal with it here at your leasure 
     } 
    } 
} 

我在一个自定义log4net的记录器使用该收集日志条目,并添加他们到一个绑定到网格的圆形数组。性能最终表现得相当不错。