2011-11-16 69 views
3

我一直在試圖創建一個寫入數據庫而不阻塞UI線程的任務。我遇到的最大問題是等待該過程完成,而不發生阻塞。等待一個漫長的過程,仍然在更新UI

我一直在努力避免使用DoEvents(雖然現在通過這個程序使用相當頻繁,但我希望在向前移動時不再使用它)。

我試圖創建進程在第二個線程上運行,並等待它完成以及使用BackgroundWorker

我有這個問題沒有代碼運行在不同的線程,但試圖找到一種方法來等待它完成。

基本上,現在我做到以下幾點:

  1. 連接到數據庫
  2. 創建一個後臺工作(或線程)的寫作做數據庫(我可能會結束與BackgroundWorker這樣我就可以使用ReportProgress
  3. 啓動線程或BackgroundWorker
  4. 使用while循環來等待線程/ BackgroundWorker完成。對於線程,我等待IsAlive成爲假,對於BackgroundWorker,我切換一個布爾變量。
  5. 我讓用戶知道該過程已完成。

問題出在#4。

否則沒有代碼while循環中,或放在Thread.Sleep(0)離開封鎖了UI(Thread.Sleep(0)使得程序走程序資源的100%爲好)

所以我做的:

while (!thread.IsAlive) 
    Thread.Sleep(1); 

- 或 -

while (bProcessIsRunning) 
    Thread.Sleep(1); 

其中阻止用戶界面。

如果我在那裏調用Application.DoEvents(),UI會被更新(儘管它是可點擊的,所以我必須在此過程運行時禁用整個表單)。

如果我同步運行這個過程,我仍然需要創建某種方式來更新UI(在我的腦海中,調用DoEvents),所以它不會被鎖定。

我在做什麼錯?

+0

歡迎(因此)。一些小事情:1。請不要在「C#.NET(3.0)」之類的東西前加上標題。那是什麼標籤用於[所以]。 2.請不要簽名。 3.說到標籤,你應該通過添加適當的標籤告訴我們你是否使用winforms或wpf或其他。 –

+0

對不起,我不知道該用什麼來標記它,用5個標籤帽,我沒有說。我正在使用Winforms(現在是一個標籤) –

+0

@JohnSaunders:我強烈反對你的建議,不要在標題中包含標籤(例如C#.NET WinForms)。原因:Google是人們進入翻頁頁面最常用的方式。擁有包含這種背景的標題使得決定問答是否相關更爲迅速。 – ToolmakerSteve

回答

4

C#使用事件模型 - 您想要做的是調度執行該工作的進程,然後讓該進程在完成時使用自定義事件或使用其中一個線程事件。當進程在「後臺」釋放控制中從代碼運行回系統時。

+0

這是否意味着我應該啓動BackgroundWorker,然後保留所有代碼執行直到RunWorkerCompleted觸發?這並不意味着用戶仍然可以與表單(與DoEvents相同)進行交互,而流程運行? 它也增加了對全局變量(如數據庫連接)的需求,如果調用函數在類中,而不是在表單本身上,則需要更多的代碼。 –

+0

有很多方法可以解決這個問題。簡單的是禁用界面,並搭上一個微調或其他視覺。然後在異步火災時將其撕下。其他選項只是「凍結」控件或在背景進行時以不同的方式處理響應。 – Hogan

+0

我不知道爲什麼這會需要更多的代碼,應該是閉包應該允許你在你的線程中使用局部變量的情況。你想看到這樣的例子嗎? – Hogan

0

我不知道這是否會簡化您的問題,但我們使用Essential Objects控件:http://www.essentialobjects.com/Products/EOWeb/來管理我們的長期流程。

+0

不幸的是,我們目前無法購買任何其他組件。儘管如此,我確實喜歡他們的一些Web界面組件。 –

+0

進度組件是免費的。 – drdwilcox

0

如果您將長數據庫操作委派給後臺工作線程,則通過從工作人員觸發ProgressChangedEvent來發送進度信息 ,您會處理這一操作,即在UI中更新進度條。同樣完成的是通過發射RunWorkerCompleteEvent來發信號。

不需要輪詢/循環需要的事件。

問題是當你的後臺線程正在做什麼,你不允許在窗體中做什麼。

關閉它,更改編輯框?等 這是通過某種狀態機完成的,可能有點像你關閉線程時禁用按鈕一樣簡單,然後在RunWorkerCompleted事件中再次啓用它。 你可以單獨離開buutom並檢查一個叫做繁忙的布爾值,它是Click處理程序。

您是否正在卸載進程以顯示進度或者僅僅是爲了避免「Windiows沒有響應」,或者在中間進程期間用戶可以合理執行其他操作,如關閉表單,取消操作等。

一旦你已經知道UI應該做什麼,你可以將backgrondworker事件綁定到一些代碼來管理事情。

+0

它主要是針對「窗口沒有響應」的消息,但是在某些時候應該有一些通知給用戶一個進程正在運行,所以他們不認爲我們已經崩潰了,我想。 –

4

首先,您爲什麼希望避免DoEvents()

其次,您使用的是衝突條款。

等待==阻擋

你說你想阻止UI線程,但你要等待任務完成。這些是互相排斥的狀態。如果你是等待完成某些事情,那麼你正在阻止你的線程。

如果你想在用戶界面實際上是可用(不堵塞),那麼你不等待你的任務完成。只需註冊一個事件處理程序即可在其結束時觸發。例如對於BackgroundWorker,請處理RunWorkerCompleted事件。對於任務,您可以使用延續來向主線程發送回調。

但似乎你只是想更新UI,不可用。通常情況下,如果你想要一個進度條或其他UI動畫繼續移動,這纔有意義。在這種情況下,我會打開一個模式對話框,啓動我的任務,然後等待它,同時,調用DoEvents()。

var dialog = new MessageBoxFormWithNoButtons("Please wait while I flip the jiggamawizzer"); 
    dialog.Shown += (_, __) => 
     { 
      var task = Task.Factory.StartNew(() => WriteToDatabase(), TaskCreationOptions.LongRunning); 
      while (!task.Wait(50)) // wait for 50 milliseconds (make shorter for smoother UI animation) 
       Application.DoEvents(); // allow UI to look alive 
      dialog.Close(); 
     } 

    dialog.ShowDialog(); 

模式對話框阻止用戶做任何事情,但任何動畫仍然會因爲的DoEvents的工作()被調用20次(或更多)。

(你可能會希望添加特殊處理不同的任務完成狀態,但這是題外話。)