簡體   English   中英

異步Post +回調真的可以防止排隊嗎?

[英]Do async Post + callbacks really prevent queuing?

我最近嘗試實現異步回調模型,這樣當開發人員使用我的庫時,他們不必擔心以下問題:

if(control.InvokeRequried) { // invoke } 

該部分代碼似乎運行得很好,但是我今天發現了一些奇怪的東西,我認為我已經診斷出該東西,但是希望了解其他有更多經驗的人的想法,以及解決該問題的方法。

我遇到的問題是,由於某些原因,我的帖子似乎很慢,因此它們似乎有點被塞住了,在我完成工作后繼續運行。 一個簡單的代碼示例:

// called by user
WorkerEventHandler workerDelegate = new WorkerEventHandler(CalcAsync);
workerDelegate.BeginInvoke(p, asyncOp, null, null);

CalcAsync(P p, AsyncOperation asyncOp)
{
   Calculate();
   asyncOp.PostOperationCompleted(new AsyncCompletedEventArgs(null, false, 
      asyncOp.UserSuppliedState);
}

Calculate()
{
   DoStuff()            // updates the progress as it goes
   this.UpdateStatus("Done");   // marks us as done
   this.UpdateProgress(pId, 100);   // increase progress to max
}

當逐步執行時,我會從發布的事件中反復調用一個UpdateProgress ,因此它們似乎無法跟上,並且仍在發布中。 這些處理方式如下:

// initalized with 
//    onProgressUpdatedDelegate = new SendOrPostCallback(UpdateProgress);    
private SendOrPostCallback onProgressUpdatedDelegate; 
public void UpdateProgress(Guid pId, ProgressEventArgs e)
{
   AsyncOperation asyncOp = GetAsyncOperation(pId);

   if (asyncOp != null)
   {
      asyncOp.Post(this.onProgressUpdatedDelegate, e);
   }
   else
   {
      UpdateProgress(this, e);
   }
}

private void UpdateProgress(object state)
{
   ProgressEventArgs e = state as ProgressEventArgs;
   UpdateProgress(this, e); // does the actual triggering of the EventHandler
}

如果相關,這些事件都將寫入控制台。 但是我似乎只看到我的進度隨着長時間的操作而下降,這似乎與通過WPF測試應用程序看到的結果相同。

這是否表明發布速度太慢? 我想我真正想要的是每次通過Post調用UpdateProgress時都想清除隊列中所有現有的帖子。 但是我不確定這可能嗎? 如果是這樣,是否有可能只為這些事件清除,因為我不想隨后意外清除我的UpdateStatus事件...

編輯

如果缺少某些方法,則更容易。

public void UpdateProgress(Guid pId, ProgressEventArgs e)
{
   AsyncOperation asyncOp = GetAsyncOperation(pId);
   if (asyncOp != null)
   {
      asyncOp.Post(this.onProgressUpdatedDelegate, e);
   }
   else
   {
      UpdateProgress(this, e);
   }
}

private void UpdateProgress(object sender, ProgressEventArgs e)
{
   ProgressChangedEventHandler handler;
   lock (progressUpdateEventLock)
   {
      handler = progressUpdateEvent;
   }

   if (handler != null)
      handler(sender, e);
}

目前尚不清楚發生了什么。 對於工作線程上的一個UpdateProgress調用,在UI線程上獲得多個UpdateProgress調用絕對不好。 您發布的代碼不應執行此操作。

但是,是的,Post()調用不是特別快。 假設它不忙於執行其他操作,則UI線程上的消息循環大約需要花費一毫秒的時間。 這不是CPU辛苦工作的一毫秒,而是由處理GetMessage()的Windows管道引起的延遲。 線程上下文切換是其中的一部分,但只是一小部分。

這可能會產生明顯的不良影響。 過於頻繁地調用Post()或使發布的委托人進行過多的辛苦工作會嚴重影響UI線程。 以至於它不再繞開常規職責了。 由於延遲,您可以快速到達那里; 每秒只需要1000個帖子。

當然,永遠不必發布這樣的消息:人眼無法感知更新的速度快於每秒25個左右的速度。 更頻繁地這樣做只是浪費精力。 但是獲得理想的更新速率並不是特別容易實現,除非工作線程特別注意經過的時間。

不管怎樣,最好將其留給客戶端代碼來告訴您它希望如何處理其通知。 FileSystemWatcher.SynchronizingObject是一個很好的例子。 如果Post()分崩離析,那是客戶端代碼解決問題的一種方法。 還要看看BackgroundWorker類。

您的示例中缺少一些代碼嗎? 您可以調用UpdateProgress(this, e) ,但是沒有具有該簽名的相應方法。 除非this是一個Guid ,否則我對此表示懷疑,因為那樣的話,您將最終獲得無限遞歸方法。 您還可以調用UpdateProgress(100) ,沒有相應的方法。

無論如何,如果您使用了探查器,或者如果沒有探查器,則使用Stopwatch來查看方法花費的時間可能會有所幫助。

線程上下文切換可能會殺死您,因為您必須執行InvokeRequired ,然后為每次更新Invoke 如果是這種情況,將Post放入隊列中並使用一個定期清空隊列的計時器可能會有所幫助。 就像是:

Timer queueTimer = new Timer(QueueTimerProc, null, 20, 20);
queueTimer.Start();

void QueueTimerProc(object state)
{
    if (queue.Count > 0)
    {
        if (this.InvokeRequired)
           // Invoke the EmptyQueue method 
        else
           // Call the EmptyQueue method
    }
}

void EmptyQueue()
{
    lock (queue)
    {
        while (queue.Count > 0)
        {
            // dequeue an item and post the update
        }
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM