2010-12-10 8 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(); 

例外:

スタートは継続タスクで呼び出されないことがあります。

私は最初のタスクを開始する必要があります。最後のタスクStartメソッドを呼び出す方法はありますか?すべてのタスクを実行するには?

+1

私はまだこの質問に答えがあるとは思わない。私はこの問題の解決策にも取り組んでいます。あなたが書いたことを強調するために:_私は仕事を構成したいだけです。それを実行するかどうかの決定はどこでもかまいません。 – Thomas

答えて

8

何も使わない理由Task.Factory.StartNewmsdn,最初のタスクは何ですか?はい、それは矛盾していますが、基本的には、継続というよりはむしろ明示的に開始されるという点で、別の種類の仕事です。

+2

タスクを作成したいだけです。それを実行するかどうかの決定はどこにでもできます。 – dotneter

+1

@dotneter:もう1つの選択肢は、クエリ式の外で初期タスクを作成し、それをクエリ式で使用して開始することです。 –

+0

しかし、私は方法からの私の複雑なタスクを返したい場合、私はContinueWith – dotneter

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:固定された第一の例...

EDIT 2追加する:

は、だから私は今、LinqToTasksは、実行中のタスクを返すために、タスクセレクタを期待していることを実現します。最初の例の2番目の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

最初の例外をスロー例外はNULLアクションを持つタスクでは呼び出されません。 2番目は大丈夫ですが、私の複雑なタスクをメソッドから戻したい場合は、2つのタスク、つまりContinueWithを作成するための最初のルートと最後のルートに対処する必要があります。 – dotneter

+0

はい、申し訳ありません - 最初の例が間違っていると投稿した直後に気づいたので、最初のタスクを別の変数に入れるように更新しました。 –

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

私が知っている限り、フレームワークが提供する開始されていないタスクを構成するには賢明な方法はありません。私が考えることができる最も簡単な解決法は、拡張メソッドです。この機能が必要な場合には、いくつかの例があります。

警告:ただ周りに渡すとラムダのトンを構成する場合と同様に、あなた自身がこれらを必要と見つけた場合、それは多くの場合、あなたのコードを簡素化するでしょう、あなたのデザインの種類が欠落していることを意味します。サブタスクを作成することで得られたことを自問してください。

/// <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)); 

前回のタスクのチェックから始まるリターンタスクが完了したかどうか。このコードは次のコードと同じように動作します

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(); 
     }); 

したがって、最初のタスクが完了していない場合、次のタスクは開始されません。

ブロックを継続して開始する必要はありません。

関連する問題