2012-03-12 16 views
10

TPLタスクを介して別のスレッドで長期実行プロセスを実行する簡単なwinformsアプリケーションがあります。この長時間実行中のプロセスでは、UI(進行状況バーなど)を更新したいと考えています。 .ContinueWith()に必須でなくてもこれを行う方法はありますか?このコードを実行するWinFormsの子タスクからUIを更新する方法

public partial class Form1 : Form 
{ 
    private Task _childTask; 

    public Form1() 
    { 
     InitializeComponent(); 

     Task.Factory.StartNew(() => 
     { 
      // Do some work 
      Thread.Sleep(1000); 

      // Update the UI 
      _childTask.Start(); 

      // Do more work 
      Thread.Sleep(1000); 
     }); 

     _childTask = new Task((antecedent) => 
     { 
      Thread.Sleep(2000); 
      textBox1.Text = "From child task"; 
     }, TaskScheduler.FromCurrentSynchronizationContext()); 


    } 
} 

私はユビキタス例外を取得:

Cross-thread operation not valid: Control 'textBox1' accessed from a thread other than the thread it was created on.

+0

スレッドにテキストボックスの参照を貼り付ける必要があると思います。 – jwillmer

答えて

10

はい、あなたは明示的に通信したいとウィンドウ/コントロールにBeginInvokeを呼び出すことができます。あなたのケースでは、これは次のようになります。

this.textBox.BeginInvoke(new Action(() => 
{ 
    this.textBox.Text = "From child task."; 
})); 
+0

これは間違いないと思われますが、コンパイルエラーが発生します。ラムダ式をデリゲート型ではないため、 'System.Delegate'型に変換できません。 – devlife

+0

匿名メソッドの代わりにアクションを作成して渡しました。なぜ上は動作しませんが、そこに道の99%を持っているかわからない。ありがとうございました。 – devlife

+1

@devlifeこれは、新しいAPIを使って作業するのに慣れていたという私のせいです。はい、古いWinForms BeginInvoke APIの署名がどのように設計されたかによって、新しいAction(...)を明示的にラップする必要があります。私は私の答えを更新します。 –

5

(あなたがそれを呼んだように、またはantecedent)あなたはstateとしてTaskSchedulerを渡しています。それはあまり意味がありません。

正確にやりたいことはわかりませんが、Taskを作成するときは、作成するときではなく、TaskSchedulerを指定する必要があります。もちろん

var scheduler = TaskScheduler.FromCurrentSynchronizationContext(); 

Task childTask = null; 

Task.Factory.StartNew(
    () => 
    { 
     Thread.Sleep(1000); 
     childTask.Start(scheduler); 
     Thread.Sleep(1000); 
    }); 

childTask = new Task(
    () => 
    { 
     Thread.Sleep(2000); 
     textBox1.Text = "From child task"; 
    }); 

、このすべてがC#5とawaitとはるかに簡単になるだろう:

public Form1() 
{ 
    InitializeComponent(); 

    StartAsync(); 
} 

private async void StartAsync() 
{ 
    // do some work 
    await Task.Run(() => { Thread.Sleep(1000); }); 

    // start more work 
    var moreWork = Task.Run(() => { Thread.Sleep(1000); }); 

    // update the UI, based on data from “some work” 
    textBox1.Text = "From async method"; 

    // wait until “more work” finishes 
    await moreWork; 
} 

あなたがすることはできません。また、childTaskは、フィールドである必要はないことと思われますasyncコンストラクタを作成しますが、そこからasyncメソッドを実行できます。 「作業中」はTask.Run()によって明示的に開始されたため、UIスレッドでは実行されません。しかし、StartAsync()が直接呼び出されたので、UIスレッドで実行されます。

+0

それは単なるポイントです。私は、もう一方のタスクを実行するために1つのタスクが完了するまで待つ必要はありません。 – devlife

+0

私が見せてくれたことをよりよく示すために私の例を更新しました。 – devlife

+0

その場合、私の最初のサンプルは引き続き動作します。私もC#5のバージョンを更新しました。 – svick

関連する問題