2017-02-06 22 views
-2

我有一個現有的系統與幾個長時間運行的操作。我希望找到一種簡單的方法來將這些操作包含在任務中,併爲用戶提供忙碌狀態通知。如何從獨立的任務更新我的winforms用戶界面?

這是使用.net 3.5,我正在使用TPL後端口。不幸的是異步/等待不能在這裏使用。

我試圖在下面顯示的一個簡單的測試表單中想出解決方案,並遇到了一些問題,包裹着我的頭。

下面的方法只顯示前2-3個任務,之後UI纔會更新,直到全部完成。爲什麼是這樣?

private void RunTaskUpdateBefore (string msg) 
{ 
UpdateUI (msg); 
Task task = Task.Factory.StartNew (() => 
{ 
    // simulate some work being done 
    Thread.Sleep (1000); 
}); 
task.Wait(); 
} 

我希望這種方法將工作,但直到所有的任務都完成

private void ChildTaskExternalWait (string msg) 
{ 
var uiScheduler = TaskScheduler.FromCurrentSynchronizationContext(); 

Task task = Task.Factory.StartNew (() => 
{ 
    Task.Factory.StartNew (() => 
    { 
     UpdateUI (msg); 
    }, System.Threading.CancellationToken.None, TaskCreationOptions.None, uiScheduler); 

    // simulate some work being done 
    Thread.Sleep (1000); 
}); 
task.Wait(); // I assume this is the problem 
} 

下面的方法效果很好,但我不知道我是否可以在使用時不更新UI我的項目。這是一個相當大的複雜系統,操作發生在許多不同的類別中,並由我們無法修改的遺留系統控制。我假設我必須嘗試創建某種全局任務隊列來執行此操作?

private Task RunTaskInternalWaitOnPrevious (string msg, Task t1 = null) 
{ 
var uiScheduler = TaskScheduler.FromCurrentSynchronizationContext(); 
Task task = Task.Factory.StartNew (() => 
{ 
    if (t1 != null) 
     { 
     t1.Wait(); 
     } 
    Task.Factory.StartNew (() => 
    { 
     UpdateUI (msg); 
    }, System.Threading.CancellationToken.None, TaskCreationOptions.None, uiScheduler); 

    // simulate some work being done 
    Thread.Sleep (2000); 
}); 

return task; 
} 

我測試的形式實現:

private void button1_Click (object sender, EventArgs e) 
{ 
// Only displays the first 2-3 tasks. After that UI doesn't get updated until all are complete 
RunTaskUpdateBefore ("Task 1"); 
RunTaskUpdateBefore ("Task 2"); 
RunTaskUpdateBefore ("Task 3"); 
RunTaskUpdateBefore ("Task 4"); 
RunTaskUpdateBefore ("Task 5"); 
RunTaskUpdateBefore ("Task 6"); 
RunTaskUpdateBefore ("Task 7"); 

// this works with any number of tasks 
// Unfortunately I'm not sure if I can use it in my application (see above notes) 
//var t1 = RunTaskInternalWaitOnPrevious ("Task 1"); 
//var t2 = RunTaskInternalWaitOnPrevious ("Task 2", t1); 
// and so on 
} 
private void UpdateUI (string msg) 
    { 
    this.textBox1.Text += msg; 
    this.textBox1.Text += System.Environment.NewLine; 
    } 

更新/澄清

*我的任務不能並行運行。
*我想通知用戶,每一步都在完成工作。
*任務運行時,沒有其他UI輸入可能發生。

這些任務沒有集中到一個特定的代碼段,並且發生在所有地方。我不知道如何描述我的情況,對不起。

+0

我認爲在這一點上,我需要從一個簡短的例子開始。 – kbeal2k

回答

2

在此代碼示例中,button1_Click將同步運行,並會阻塞UI線程,直到它完成爲止,因此即使調用的方法中創建的任務想要更新UI線程,我也懷疑這些嘗試正在排隊,直到一切完成後。就你而言,等待和異步是爲解決這個問題而設計的。在此之前,BackgroundWorkers可以用來做同樣的事情。我將爲您的表單添加一個BackgroundWorker,將所有代碼從button1_Click移動到BackgroundWorker的DoWork方法,並在單擊Button1時啓動後臺工作器。我相信,BackgroundWorkers已被棄用,但與.net做工精細3.5

這裏是關於BackgroundWorker的類快速教程,如果你不熟悉它:https://www.dotnetperls.com/backgroundworker

插入Application.DoEvents暫停的button1_Click的執行,直到UI可以自行更新是另一種選擇,但這種方法有很多潛在的問題。看到這個線程你長期公平的警告名單:Use of Application.DoEvents()

0

當運行多個線程提供要顯示的進度信息,你可以進行如下:

  • 定義包含任務ID的updateInfo時類進度 信息,
  • 創建一個變量UpdateInfos =新名單< updateInfo時>(),
  • 當你想從一個任務發送進度信息,創建和填充 的updateInfo時,然後將其推該列表,即鎖(UpdateInfos){ UpdateInfos.Add(...);}
  • 創建一個定時器(例如,每250ms),並在計時器事件中彈出所有UpdateInfo並相應地刷新HMI。
-1

您可以使用continueWith在任務完成後立即更新線程。

var task1 = new Task(() => Thread.Sleep(1000)); //replace with actual task 

var task2 = task1.ContinueWith(t => UpdateUI(msg), CancellationToken.None, TaskContinuationOptions.None, uiScheduler); 

task1.Start(); 
+0

TPL不是.Net 3.5的選項 – VMAtm

相關問題