2010-03-04 21 views
3

我遇到了一個問題,我可以用Application.DoEvents修復,但不想離開它,因爲它可能會引入各種討厭的問題。想要調用相同的BackgroundWorker多次沒有使用Application.DoEvents

背景: 我們的應用程序主要是一個桌面應用程序,它可以對Web服務進行多次調用。我們控制着一切,但整體系統設計的變化不會被認真考慮。其中一個調用Calculate經常使用,偶爾可能需要幾分鐘時間來處理所有數據以返回有效結果。

此前對Calculate的調用是同步完成的,因此會阻止用戶界面讓用戶懷疑應用程序是否已凍結等。我已成功將所有長等待調用移動到BackgroundWorker,然後創建簡單的等待屏幕可以循環顯示「正在計算...」動畫消息。

現在問題出現時,我們的UI代碼嘗試在第一次完成之前再次調用計算例程。我會得到一個「此BackgroundWorker目前正忙,無法運行多個實例...」消息。我認爲應該由resetEvent.WaitOne()調用來控制。它沒有,所以我想可能是另一個控制整個例程訪問的事件會有所幫助,所以我添加了calcDoneEvent。這仍然沒有解決問題,但會導致它在第二次調用Calculate的calcDoneEvent.WaitOne()調用時無限期地阻塞。然後在奇想中,我將Application.DoEvents添加到Calculate和viola的底部,解決了問題。

我不想在那裏留下那個.DoEvents,因爲我讀過它會導致以後很難追查的問題。有沒有更好的方法來處理這種情況?

在此先感謝..

Private WithEvents CalculateBGW As New System.ComponentModel.BackgroundWorker 
Dim resetEvent As New Threading.AutoResetEvent(False) 
Dim calcDoneEvent As New Threading.AutoResetEvent(True) 

Public Sub Calculate() 

    calcDoneEvent.WaitOne() ' will wait if there is already a calculate running.' 
    calcDoneEvent.Reset() 

    ' setup variables for the background worker' 

    CalculateBGW.RunWorkerAsync() ' Start the call to calculate' 

    Dim nMsgState As Integer = 0 
    ' will block until the backgorundWorker is done' 
    Do While Not resetEvent.WaitOne(200) ' sleep for 200 miliseconds, then update the status window' 
     Select Case nMsgState 
      Case 1 
       PleaseWait(True, vbNull, "Calculating. ") 
      Case 2 
       PleaseWait(True, vbNull, "Calculating.. ") 
      Case 3 
       PleaseWait(True, vbNull, "Calculating... ") 
      Case 4 
       PleaseWait(True, vbNull, "Calculating....") 
      Case Else 
       PleaseWait(True, vbNull, "Calculating ") 
     End Select 
     nMsgState = (nMsgState + 1) Mod 5 
    Loop 

    PleaseWait(False, vbNull) 'make sure the wait screen goes away' 

    calcDoneEvent.Set() ' allow another calculate to proceed' 
    Application.DoEvents() ' I hate using this here' 
End Sub 

Private Sub CalculateBGW_DoWork(ByVal sender As System.Object, _ 
    ByVal e As System.ComponentModel.DoWorkEventArgs) Handles CalculateBGW.DoWork 
    Try 
     'make WS Call, do data processing on it, can take a long time..' 
     'No Catch inside the DoWork for BGW, or exception handling wont work right...' 
     'Catch' 
    Finally 
     resetEvent.Set() 'unblock the main thread' 
    End Try 
End Sub 

Private Sub CalculateBGW_RunWorkerCompleted(ByVal sender As Object, _ 
    ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles CalculateBGW.RunWorkerCompleted 

    'If an error occurs we must check e.Error prior to touching e.Result, or the BGW' 
    'will possibly "eat" the exception for breakfast (I hear theyre tasty w/ jam)' 
    If Not (e.Error Is Nothing) Then 

     'If a Web Exception timeout, retry the call' 
     If TypeOf e.Error Is System.Net.WebException And _ 
      e.Error.Message = "The operation has timed out" And _ 
      intRetryCount < intRetryMax Then 

      ' Code for checking retry times, increasing timeout, then possibly recalling the BGW' 

      resetEvent.Reset() 
      CalculateBGW.RunWorkerAsync() 'restart the call to the WS' 
     Else 
      Throw e.Error ' after intRetryMax times, go ahead and throw the error up higher' 
     End If 
    Else 
     Try 

      'normal completion stuff' 

     Catch ex As Exception 
      Throw 
     End Try 
    End If 

End Sub 

回答

4

您宣佈:

Private WithEvents CalculateBGW As New System.ComponentModel.BackgroundWorker 
Dim resetEvent As New Threading.AutoResetEvent(False) 
Dim calcDoneEvent As New Threading.AutoResetEvent(True) 

私人領域包含類的。請注意,通過這種方式,所有對RunWorkerAsync()的調用都被稱爲BackgroundWorker類的相同對象實例(即對同一對象)。這就是爲什麼它「很忙」。此代碼的構建目的是在給定時間僅保存一個BackgroundWorker

如果您的意思是允許UI代碼在需要時調用Calculate()方法,則應在Calculate()方法中聲明CalculateBGW作爲局部變量,從而在每次調用時創建一個BackgroundWorker類的新實例並且它們將異步運行)。這意味着您必須使用AddHandlerRemoveHandler來添加和刪除Calculate()中的事件處理程序。

有幾種方法可以更新UI上的進度,但建議使用BackgroundWorker.ProgressChanged事件和BackgroundWorker.ReportProgress方法。

使用BackgroundWorker.RunWorkerCompleted事件作爲回調觸發器,報告UI計算完成,從而觸發所需的代碼來表示結果。這種方法消除了維護圍繞計算線程的線程循環的需要 - 從而消除了DoEvents()的需要。它讓計算線程在其完成工作時通知其老闆,而不是讓老闆檢查工人的狀態並反覆睡覺。

+0

我認爲你只是可能會在那裏。如果在這臺古老的計算機上重新打開IDE,並不需要很長時間,我現在就可以嘗試,但它會一直等到第一天。 – 2010-03-04 23:48:03

相關問題