2011-07-30 35 views
5

Tôi đang chuyển đổi một số mã async/await thành chuỗi nhiệm vụ, vì vậy tôi có thể sử dụng nó trong khung công tác đã phát hành. Mã chờ đợi trông như thế nàyChuỗi nhiệm vụ không có TaskCompletionSource?

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

nơi

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

Tôi đã hy vọng để chuỗi InvokeMemorize để trả lại nhiệm vụ sản xuất bởi Memorize, nhưng có kết quả trong một Task<Task<TraumMessage>. Các giải pháp tôi đã kết thúc là một TaskCompletionSource<TraumMessage> như tín hiệu của tôi:

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

Có cách nào để thực hiện điều này mà không có sự TaskCompletionSource?

Trả lời

1

Tôi nghĩ đó là cách duy nhất để hoàn thành những gì bạn muốn. Việc kết hợp các nhiệm vụ khác nhau với nhau không được hỗ trợ bởi các API tiếp tục, vì vậy bạn phải sử dụng một số TaskCompletionSource như bạn phải phối hợp công việc.

Tôi không cài đặt Async CTP trên máy này, nhưng tại sao bạn không xem mã bằng trình giải mã (hoặc ILDASM nếu bạn biết cách đọc IL) để xem nó đang làm gì. Tôi đặt cược nó làm một cái gì đó rất giống với mã TCS của bạn dưới bìa.

+0

Nhờ loạt bài EduAsync của Jon Skeet, tôi đã xem xét dưới sự che chở của async/await và về cơ bản nó xây dựng một lớp chạy một máy trạng thái. Tôi đã sử dụng cách tiếp cận đó trước đây, nhưng nó thậm chí còn tẻ nhạt hơn cách tiếp cận TCS. –

+0

Ah, tốt, tôi nghĩ về điều này một bó nhiều hơn và không có cách nào với sự tiếp tục thuần túy trừ khi bạn muốn chặn việc tiếp tục nhiệm vụ thứ nhất đang chờ kết quả của việc tiếp tục nhiệm vụ thứ hai. Nếu các ngôi sao được sắp xếp làm việc có thể có nghĩa là có rất ít chi phí trong đó, nhưng vì bạn về mặt lý thuyết không có đầu mối như thế nào nhiệm vụ thứ 2 được tạo ra (có thể là một kế hoạch nhiệm vụ hoàn toàn khác hoặc APM chẳng hạn) không có bảo đảm cho điều đó. Vì vậy, bạn phải thừa nhận rằng bạn đang chặn một chuỗi đang chờ kết quả. Nếu bạn muốn xem những gì tôi có nghĩa là cho tôi biết và tôi sẽ cập nhật câu trả lời của tôi. –

+1

Tại http://blogs.msdn.com/b/pfxteam/archive/2010/11/21/10094564.aspx Stephen Taub kết thúc tốt đẹp 'TaskCompletionSource' trong phương thức trợ giúp có tên 'Then' để triển khai mẫu này. – Govert

0

Bạn có thể sử dụng các tác vụ con đính kèm. Nhiệm vụ phụ huynh sẽ chỉ chuyển sang trạng thái đã hoàn thành khi tất cả các nhiệm vụ con hoàn tất. Các ngoại lệ được truyền cho nhiệm vụ phụ huynh. Bạn sẽ cần một người giữ kết quả, vì kết quả sẽ được chỉ định sau khi số ủy nhiệm của nhiệm vụ phụ huynh đã hoàn tất, nhưng sẽ được đặt khi nhiệm vụ phụ huynh tiếp tục được chạy.

Như thế này:

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

Vâng, khuôn khổ đi kèm với một phương pháp mở rộng tiện dụng Unwrap() cho chính xác những gì bạn muốn.

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

Nếu bạn đang hủy thì bạn cần chuyển thẻ thông báo hủy vào địa điểm thích hợp.

Các vấn đề liên quan