2008-09-24 33 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的記錄器使用該收集日誌條目,並添加他們到一個綁定到網格的圓形數組。性能最終表現得相當不錯。