2011-07-30 65 views
5

我正在將一些異步/等待代碼轉換爲鏈接任務,因此我可以在發佈的框架中使用它。該AWAIT代碼看起來像這樣任務鏈接沒有TaskCompletionSource?

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

其中

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

我希望鏈InvokeMemorize返回由Memorize產生的任務,但導致Task<Task<TraumMessage>。我已經結束了該解決方案是一個TaskCompletionSource<TraumMessage>我的信號:

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

有沒有辦法做到這一點沒有TaskCompletionSource

回答

1

我認爲這是實現你想要的東西的唯一方法。持續不斷的連接任務不被延續API支持,因此您必須訴諸於使用像您一樣協調工作的TaskCompletionSource

我沒有在這臺機器上安裝Async CTP,但爲什麼不用帶反編譯器(或者ILDASM,如果你知道如何讀IL)來查看代碼,看看它在做什麼。我敢打賭,它的做法與您的TCS代碼非常相似。

+0

感謝Jon Skeet的EduAsync系列,我查看了async/await的底層,它基本上構建了一個運行狀態機的類。我之前也使用過這種方法,但比TCS方法更乏味。 –

+0

啊,好吧,我還想過這麼多,除非你想阻止等待第二項任務繼續進行的第一項任務的繼續,否則純粹的繼續是無法實現的。如果星星是對齊的,那麼盜取工作可能意味着其中的開銷很小,但由於理論上不知道第二個任務是如何創建的(可能完全是差異化任務調度程序或APM),因此無法保證。所以你必須假設你會阻止等待結果的線程。如果你想看看我的意思是讓我知道,我會更新我的答案。 –

+1

在http://blogs.msdn.com/b/pfxteam/archive/2010/11/21/10094564.aspx Stephen Taub將'TaskCompletionSource'封裝在名爲'Then'的助手方法中以實現此模式。 – 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(); 

如果你正在取消,那麼顯然你需要將取消令牌傳遞到適當的位置。