2012-06-24 22 views
3

是否有可能鏈接具有不同返回類型或根本沒有返回類型的任務?例如,在僞代碼:具有不同返回類型的鏈接任務

Task<double>.ContinueWith(Task<string>).ContinueWith(Task<String>).ContinueWith(Task) 

或者也在這裏是真實的代碼示例:

private double SumRootN(int root) 
{ 
    double result = 0; 
    for (int i = 1; i < 10000000; i++) 
    { 
     result += Math.Exp(Math.Log(i)/root); 
    } 
    return result; 
} 

private void taskSequentialContinuationButton_Click(object sender, RoutedEventArgs e) 
{ 
    Task<double> task = null; 

    this.statusText.Text = ""; 
    this.watch = Stopwatch.StartNew(); 
    for (int i = 2; i < 20; i++) 
    { 
     int j = i; 
     if (task == null) 
     { 
      task = Task<double>.Factory.StartNew(() => { return SumRootN(j); }); 
     } 
     else 
     { 
      task = task.ContinueWith((t) => { return SumRootN(j); }); 
     } 

     task = task.ContinueWith((t) => 
     { // I don't want to return anything from this task but I have to, to get it to compile 
      this.statusText.Text += String.Format("root {0} : {1}\n", j, t.Result); 
      return t.Result; 
     }, TaskScheduler.FromCurrentSynchronizationContext()); 
    } 

    task.ContinueWith((t) => 
     { // I also don't want to return anything here but I don't seem to have to here even though intellisense expects a Task<double>?? 
      this.statusText.Text += String.Format("{0}ms elapsed\n", watch.ElapsedMilliseconds); 
     }, TaskScheduler.FromCurrentSynchronizationContext()); 
} 

請參閱鏈接的怪事內嵌批註。

+0

爲什麼你有這麼多'任務'的順序?難道你不能在一個Task中運行循環,這會創建'SynchronizationContext'' Task's? – svick

+0

這確實是一個更好的解決方案,但是如何在不同的線程上獲得單個任務中的UI線程上下文?我知道這可以用Dispatcher完成,但是這會使代碼依賴於UI框架。是否有可能傳入對UI線程的引用?你能做一個代碼示例嗎? – flolim

+0

我認爲這裏的代碼示例可能會造成更多的傷害。你能從運行時行爲的角度解釋你正在尋找什麼?由於您的ContinueWith調用不使用't'或't.Result' –

回答

2

如果你想鏈Task s的返回類型不同,你可以只是把他們每個人在不同的變量:

Task<Type1> task1 = Task.Factory.StartNew(() => Compute1()); 
Task<Type2> task2 = task1.ContinueWith(_ => Compute2()); 
Task<Type3> task3 = task2.ContinueWith(_ => Compute3()); 

針對您的特殊情況下,當你計算在一個循環的東西,希望每個迭代之後的UI線程上報告,您可以這樣做:

var uiScheduler = TaskScheduler.FromCurrentSynchronizationContext(); 

Task.Factory.StartNew(() => 
{ 
    for (int i = 2; i < 20; i++) 
    { 
     // perform computation 

     Task.Factory.StartNew(() => 
     { 
      // report result 
     }, CancellationToken.None, TaskCreationOptions.None, uiScheduler); 
    } 
}); 
+0

謝謝,這正是我所期待的。 – flolim

+0

FWIW,如果您想在循環中報告進度,則只需使用BackgroundWorker就可以更簡單。 –

+0

@JamesManning如果那真的是你想要做的,那麼是的。但使用'任務'意味着如果你想改變任何東西,它很可能會更容易。 – svick

0

您的代碼很複雜,因爲您重複使用了一個task變量。看起來你想要啓動一些任務並等待它們完成。像這樣的東西應該工作:

SynchronizationContext context = SynchronizationContext.Current; 

Task[] tasks = Enumerable.Range(2, 19) 
    .Select(d => Task<double>.Factory.StartNew(() => SumRootN(d)) 
     .ContinueWith(t => { 
      this.statusText.Text += string.Format("root {0} : {1}\n", d, t.Result); 
     })) 
    .ToArray(); 

Task.Factory.StartNew(() => { 
    Task.WaitForAll(tasks); 
    string msg = string.Format("{0}ms elapsed\n", watch.ElapsedMilliseconds); 
    context.Post(_ => { this.statusText.Text += msg; }, null); 
}); 

編輯:如果你想創建的任務鏈,這可能工作:

Task first = new Task(() => { }); 
Task outer = Enumerable.Range(2, 19) 
    .Aggregate(first, (task, d) => { 
     Task inner = new Task<double>(() => SumRootN(d)) 
      .ContinueWith(rt => { 
       this.statusText.Text += String.Format("root {0} : {1}\n", d, rt.Result); 
      }); 
     return task.ContinueWith(inner); 
    }); 

outer.ContinueWith(t => { 
    this.statusText.Text += String.Format("{0}ms elapsed\n", watch.ElapsedMilliseconds); 
}); 

first.Start(); 
+1

這會在代碼執行時凍結UI。你在'SynchronizationContext'上真的不應該'wait()'(或'WaitForAll()')。 – svick

+0

該代碼確實比較簡單,但我試圖將任務鏈接在一起,以便在後臺執行*順序*。我的意思是做SumRootN(2) - >在UI中顯示結果 - > SumRootN(3) - >在UI中顯示結果 - > ... - >顯示總時間。 我知道並行執行它們要快得多,但實際上我只是試圖舉例說明如何連接順序鏈(可能鏈中的每個「鏈接」具有不同的返回類型)。 – flolim

+0

http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker。aspx –