2010-12-10 104 views
18

我的代碼:有什麼方法可以使用ContinueWith任務來啓動任務嗎?

var r = from x in new Task<int>(() => 1) 
     from y in new Task<int>(() => x + 1) 
     select y; 
r.ContinueWith(x => Console.WriteLine(x.Result)).Start(); 

new Task<int>(() => 1) 
    .ContinueWith(x => x.Result + 1) 
    .ContinueWith(x => Console.WriteLine(x.Result)) 
    .Start(); 

例外:

開始可能無法在後續任務中調用。

所以我要開始第一個任務。有什麼辦法可以調用最後一個任務啓動方法來運行所有任務?

+1

我還是不認爲這個問題有答案,真的。我正在爲這個問題制定解決方案。強調你寫的東西:_我只是想編寫任務。關於運行或不運行的決定可以在任何地方_ – Thomas 2016-05-23 20:05:16

回答

8

任何理由不使用Task.Factory.StartNewmsdn,ms docs爲第一任務?是的,它是不一致的 - 但它從根本上說是一種不同的任務,就是明確開始而不僅僅是一個延續。

+2

我只是想編寫任務。關於是否運行的決定可以在任何地方。 – dotneter 2010-12-10 13:33:29

+1

@dotneter:另一個選擇是在查詢表達式之外創建初始任務,然後在查詢表達式中使用它,然後啓動它。 – 2010-12-10 13:38:26

+0

但是,如果我想從方法中返回我的複雜任務,我需要處理兩個任務,開始根和最後一個與ContinueWith組成 – dotneter 2010-12-10 13:44:44

2

我真的不知道有什麼錯只是寫這樣的:

var t1 = new Task<int>(() => 1) 
var r = from x in t1 
     from y in new Task<int>(() => x + 1) 
     select y; 
r.ContinueWith(x => Console.WriteLine(x.Result)); 
t1.Start(); 

或本:

var t = new Task<int>(() => 1) 
t.ContinueWith(x => x.Result + 1) 
.ContinueWith(x => Console.WriteLine(x.Result)) 
t.Start(); 

直接表達你真正想做的事。 (這是你想要開始的首要任務,那麼在初始任務中調用Start有什麼問題?)爲什麼你在尋找一個模糊的語法?

編輯:固定第一個例子...

EDIT 2補充:

所以我現在LinqToTasks預計任務選擇返回正在運行的任務實現。因此,第一個示例中的第二個from子句將返回一個不會運行的任務。所以,你真正需要的是這樣的:

var t1 = new Task<int>(() => 1); 
var r = from x in t1 
     from y in Task<int>.Factory.StartNew(() => x + 1) 
     select y; 
r.ContinueWith(x => Console.WriteLine(x.Result)); 
t1.Start(); 

閒來無事是要呼籲這些from條款所產生的任務Start。由於相關選擇器在上一個任務完成之前並未實際執行,因此您仍然可以控制何時啓動根任務。這似乎是工作,但它是相當難看

。但這似乎是LinqToTasks的設計方式......我想我會堅持使用常規的函數調用語法。

+0

第一個,拋出異常開始可能不會被調用一個空作用的任務。第二個是好的,但是如果我想從方法中返回我的複雜任務,我需要處理兩個任務,開始時爲根,最後一個時間爲與ContinueWith組合。 – dotneter 2010-12-10 15:06:15

+0

對不起 - 我發佈後立即意識到我的第一個例子是錯誤的,所以我更新它將最初的任務放在一個單獨的變量中。 – 2010-12-10 16:02:53

0

的問題是,與LINQ 選擇任務只會造成一個表達式樹!

因此,這裏是你需要做什麼:

var query = 
    from i in Enumerable.Range(1, 4) 
    let task = Task.Factory.StartNew(() => Tuple.Create(i, IsPrime(i))) // put a breakpoint here 
    select task.ContinueWith(delegate { 
     Console.WriteLine("{0} {1} prime.", _.Result.Item1, _.Result.Item2 ? "is" : "is not"); 
    }); 
// breakpoint never hit yet 
query.ToArray(); // breakpoint hit here 4 times 
// all tasks are now running and continuations will start 
TaskEx.Await(query.ToArray()); // breakpoint hit 4 more times!! 
0

今天我有同樣的問題。我想創建一個包裝任務來處理來自內部任務的錯誤。這是我想出了:

var yourInitialTask = new Task(delegate 
{ 
    throw e; 
}); 

var continuation = task.ContinueWith(t => 
{ 
    if (task.IsCanceled) 
    { 
     Debug.WriteLine("IsCanceled: " + job.GetType()); 
    } 
    else if (task.IsFaulted) 
    { 
     Debug.WriteLine("IsFaulted: " + job.GetType()); 
    } 
    else if (task.IsCompleted) 
    { 
     Debug.WriteLine("IsCompleted: " + job.GetType()); 
    } 
}, TaskContinuationOptions.ExecuteSynchronously); //or consider removing execute synchronously if your continuation task is going to take long 

var wrapper = new Task(() => 
{ 
    task.Start(); 
    continuation.Wait(); 
}); 

return wrapper; 

這裏的關鍵特點是,只要你想 -the包裝是可啓動 -the延續部分原始任務之後運行。使用ContineWith()創建的延期任務不可啓動。

這個例子的一個不太重要的特性是,這個異常被記錄和丟棄(解決了我的問題,而不是你的問題)。在繼續發生異常時,您可能希望做一些不同的事情,例如將其重新拋出爲當前任務的例外,以便將其排除。

0

據我所知,沒有合理的方法來編寫由框架提供的非啓動任務。我能想到的最簡單的解決方案是擴展方法。如果您需要此功能,以下是您可以構建的一些示例。

警告:就像傳遞和編寫大量的lambda一樣,如果您發現自己需要這些,通常意味着您在設計中缺少一種可以簡化代碼的類型。問自己通過創建子任務獲得了什麼。

/// <summary> 
/// Compose tasks without starting them. 
/// Waiting on the returned task waits for both components to complete. 
/// An exception in the first task will stop the second task running. 
/// </summary> 
public static class TaskExtensions 
{ 
    public static Task FollowedBy(this Task first, Task second) 
    { 
     return FollowedBy(first, 
      () => 
      { 
       second.Start(); 
       second.Wait(); 
      }); 
    } 

    public static Task FollowedBy(this Task first, Action second) 
    { 
     return new Task(
      () => 
      { 
       if (first.Status == TaskStatus.Created) first.Start(); 
       first.Wait(); 
       second(); 
      }); 
    } 

    public static Task FollowedBy<T>(this Task first, Task<T> second) 
    { 
     return new Task<T>(
      () => 
      { 
       if (first.Status == TaskStatus.Created) first.Start(); 
       first.Wait(); 
       second.Start(); 
       return second.Result; 
      }); 
    } 

    public static Task FollowedBy<T>(this Task<T> first, Action<T> second) 
    { 
     return new Task(
      () => 
      { 
       if (first.Status == TaskStatus.Created) first.Start(); 
       var firstResult = first.Result; 
       second(firstResult); 
      }); 
    } 

    public static Task<TSecond> FollowedBy<TFirst, TSecond>(this Task<TFirst> first, Func<TFirst, TSecond> second) 
    { 
     return new Task<TSecond>(
      () => 
      { 
       if (first.Status == TaskStatus.Created) first.Start(); 
       return second(first.Result); 
      }); 
    } 
} 
0

答案很簡單。 ContinueWith是自動啓動任務。第一項任務需要運行。

var r= Task<int>.Run<int>(() => 1) 
      .ContinueWith<int>(x => x.Result + 1) 
      .ContinueWith(x => Console.WriteLine(x.Result)); 

ContinueWith以檢查前一個任務開始的返回任務完成與否。以同樣的方式該代碼工作像下面的代碼

var firstTask = new Task<int>(() => 1); 
     firstTask.Start(); 
     var firstawaiter = firstTask.GetAwaiter(); 
     var secondTask = new Task<int>(x => (int)x + 1 , firstawaiter.GetResult()); 

     firstawaiter.OnCompleted(() => 
     { 
      secondTask.Start(); 
     }); 

     var secondawaiter = secondTask.GetAwaiter(); 


     var thirdTask = new Task(x => Console.WriteLine(x) , secondawaiter.GetResult()); 

     secondawaiter.OnCompleted(() => 
     { 
      thirdTask.Start(); 
     }); 

所以,如果沒有完成第一個任務,下一個任務將不會啓動。

而且你不需要開始繼續塊。

相關問題