2011-07-30 5 views
5

私はいくつかの非同期/待機しているコードを連鎖したタスクに変換しているので、リリースされたフレームワークで使用できます。 awaitコードはTaskCompletionSourceのないタスクチェーン?

Task<TraumMessage> Invoke(string verb) {} 
Task<TraumMessage> Memorize() {} 

私はMemorizeで作成したタスクを返すためにチェーンInvokeMemorizeに期待していたが、Task<Task<TraumMessage>における結果は、この

public async Task<TraumMessage> Get() { 
    var message = await Invoke("GET"); 
    var memorized = await message.Memorize(); 
    return memorized; 
} 

のように見えます。私が終わってきたソリューションは、私の信号としてTaskCompletionSource<TraumMessage>は次のとおりです。TaskCompletionSourceせずにこれを実現する方法は

public Task<TraumMessage> Get() { 
    var completion = new TaskCompletionSource<TraumMessage>(); 
    Invoke("GET").ContinueWith(t1 => { 
    if(t1.IsFaulted) { 
     completion.SetException(t1.Exception); 
     return; 
    } 
    t1.Result.Memorize().ContinueWith(t2 => { 
     if(t2.IsFaulted) { 
     completion.SetException(t2.Exception); 
     return; 
     } 
     completion.SetResult(t2.Result); 
    }); 
    }); 
    return completion.Task; 
} 

ありますか?

答えて

1

私はそれがあなたが望むものを達成する唯一の方法だと思います。異種のタスクを連鎖させることは、継続APIではサポートされていないため、作業を調整する必要があるようにTaskCompletionSourceを使用する必要があります。

このマシンにはAsync CTPがインストールされていませんが、逆コンパイラ(またはILの読み方を知っている場合はILDASM)を使ってコードを調べてみてください。私はそれがあなたのTCSコードに非常によく似ています。

+0

Jon SkeetのEduAsyncシリーズのおかげで、私はasync/awaitのフードの下で見ました。そして、それは基本的にステートマシンを実行するクラスを構築します。私はこれまでもこのアプローチを使用してきましたが、TCSのアプローチよりもずっと面倒です。 –

+0

ああ、これについてもっと考えてみましたが、第2のタスクの継続の結果を待っている第1のタスクの継続をブロックしない限り、純粋な継続の方法はありません。もし星が一直線に並んでいれば、盗みはほとんどないかもしれませんが、理論的には2番目のタスクがどのように作成されているかを知ることはできません(タスクスケジューラやAPMなど)。したがって、結果を待っているスレッドをブロックすると仮定しなければなりません。もし私が何を意味しているか知りたいのであれば、私は答えを更新します。 –

+1

http://blogs.msdn.com/b/pfxteam/archive/2010/11/21/10094564.aspx Stephen Taubはこのパターンを実装するために 'Then'というヘルパーメソッドで 'TaskCompletionSource'をラップします。 – Govert

0

添付された子タスクを使用できます。すべての子タスクが完了すると、親タスクは完了ステータスに遷移します。例外は親タスクに伝播されます。 親タスクのデリゲートが終了した後に結果がに割り当てられますが、親タスクの継続が実行されたときに結果が設定されるため、結果保持者が必要です。このよう

public class Holder<T> where T: class 
{ 
    public T Value { get; set; } 
} 

public Task<Holder<TraumMessage>> Get() { 
    var invokeTask = Invoke("GET"); 
    var result = invokeTask.ContinueWith<Holder<TraumMessage>>(t1 => { 
    var holder = new Holder<TraumMessage>(); 
    var memorizeTask = t1.Result.Memorize(); 
    memorizeTask.ContinueWith(t2 => { 
     holder.Value = t2.Result; 
    }, TaskContinuationOptions.AttachedToParent); 
    return holder; 
    }); 
    return result; 
} 
4

はい、フレームワークは、正確に何をしたいのための便利なアンラップ()拡張メソッドが付属しています。

Invoke("GET").ContinueWith(t => t.Result.Memorize()).Unwrap(); 

キャンセルを行っている場合は、キャンセルトークンを適切な場所に渡す必要があります。