2013-11-26 24 views
0

下面的代碼是我遇到的問題的簡單示例。在表單加載時會發生什麼 - For循環會在每次迭代中創建一個新任務,然後進入'if(pic.InvokeRequired)'部分,但會返回到For循環並繼續迭代在調用pic.BeginInvoke()之後,任務會經過它們各自對method()的調用。如何正確地等待任務使用多線程完成調用?

我想實現的是調用完成它的第二遍方法()並最終將pic.BackColor更改爲Color.Blue。我想這是可能的,不是嗎?我花了幾個小時尋找並找不到滿意的答案。

要自己運行此代碼,請創建一個新的WinForms應用程序項目(我正在使用Visual Studio 2012)並添加一個PictureBox叫做'pic'。插入下面的代碼。

謝謝你的幫助!

private void Form1_Load(object sender, EventArgs e) 
{ 
    for (int i = 0; i < 5; i++) 
    { 
     Task task = Task.Factory.StartNew(() => method()); 
     task.Wait(); //Waits for task to complete before proceeding 
    } 
} 

public delegate void delegate_method(); 
private void method() 
{ 
    if (pic.InvokeRequired) 
    { 
     delegate_method dm = new delegate_method(() => method()); 
     pic.BeginInvoke(dm); //If ran once (without the loop in Form1_Load) it would call 'method()' immediately from here. 
    } 
    else 
    { 
     pic.BackColor = Color.Blue; 
    } 
} 
+0

改爲使用Form.Activated事件。 – thepirat000

+0

仍然與交叉線程衝突。如果合併Form.Activate事件爲您工作,您可以發佈我的示例的修改嗎? – user3034748

+0

我正在閱讀這個問題的方式是,當你等待一個異步調用完成時,你想要做的就是阻塞。唯一能夠做到這一點的方法(至少我能在這種情況下看到的唯一方式)是用'while(!task.IsCompleted)Application.DoEvents()替換你的'task.Wait() );''和'pic.BeginInvoke(dm);''pic.Invoke(dm);',由於Application.DoEvents()的惡意特性,我絕對不建議這樣做。相反,你應該看看如何讓你的代碼正確地調用'method()',以避免阻塞(即task.Wait()))。 –

回答

0

我可以,因爲在某些情況下,阻止可能最好充分在異步(儘管它更是一個不得已的舉措的)升值的問題。

這裏的答案取決於您的異步調用(在您調用UI線程上的操作之前在後臺發生的工作的實際內容 - 我假設您選擇的代碼部分爲簡單起見省略)執行任何UI綁定的工作。如果是這樣,你運氣不好,使用Application.DoEvents()是你最好的選擇。

然而,如果不是這樣,你必須在異步代碼足夠的控制,你可以做的是,而不是試圖從你的任務中調用UI ,通過Action描述下班回來給你UI線程(如您的TaskResult),然後在那裏處理其調用。

請注意,我已經簡化了Method()的實現,因爲它不再從非UI線程中調用。

警告:簡化代碼,不適合生產。

using System; 
using System.Diagnostics; 
using System.Drawing; 
using System.Linq; 
using System.Threading.Tasks; 
using System.Windows.Forms; 

namespace PicInvoke 
{ 
    public partial class Form1 : Form 
    { 
     public Form1() 
     { 
      this.InitializeComponent(); 
     } 

     private void Form1_Load(object sender, EventArgs e) 
     { 
      Stopwatch sw; 

      // Sequential processing. 
      sw = Stopwatch.StartNew(); 

      this.DoWorkAndBlockSequential(); 

      sw.Stop(); 

      MessageBox.Show(string.Format("Sequential work complete. Time taken: {0:0.000}s.", (double)sw.ElapsedMilliseconds/1000.0)); 

      // Parallel processing. 
      sw = Stopwatch.StartNew(); 

      this.DoWorkAndBlockParallel(); 

      sw.Stop(); 

      MessageBox.Show(string.Format("Parallel work complete. Time taken: {0:0.000}s.", (double)sw.ElapsedMilliseconds/1000.0)); 
     } 

     private void DoWorkAndBlockSequential() 
     { 
      for (int i = 0; i < 5; i++) 
      { 
       var task = this.DoWorkAsync(); 

       // Block the UI thread until the task has completed. 
       var action = task.Result; 

       // Invoke the delegate. 
       action(); 
      } 
     } 

     private void DoWorkAndBlockParallel() 
     { 
      var tasks = Enumerable 
       .Range(0, 5) 
       .Select(_ => this.DoWorkAsync()) 
       .ToArray(); 

      // Block UI thread until all tasks complete. 
      Task.WaitAll(tasks); 

      foreach (var task in tasks) 
      { 
       var action = task.Result; 

       // Invoke the delegate. 
       action(); 
      } 
     } 

     private Task<Action> DoWorkAsync() 
     { 
      // Note: this CANNOT synchronously post messages 
      // to the UI thread as that will cause a deadlock. 
      return Task 
       // Simulate async work. 
       .Delay(TimeSpan.FromSeconds(1)) 
       // Tell the UI thread what needs to be done via Task.Result. 
       // We are not performing the work here - merely telling the 
       // caller what needs to be done. 
       .ContinueWith(
        _ => new Action(this.Method), 
        TaskContinuationOptions.ExecuteSynchronously); 
     } 

     private void Method() 
     { 
      pic.BackColor = Color.Blue; 
     } 
    } 
} 
+0

基里爾,非常感謝。我從你發佈的例子中獲得了很多,對我來說,解決我的問題已經足夠了。我感謝您的幫助。 – user3034748