2014-06-26 18 views
13

Task<T> gọn gàng giữ một "đã bắt đầu, có thể được hoàn thành" tính toán, có thể được sáng tác với các tác vụ khác, ánh xạ với các chức năng, vv Ngược lại, F # async monad giữ một "có thể bắt đầu sau, có thể đang chạy ngay bây giờ" tính toán , cùng với a CancellationToken. Trong C#, bạn thường phải xếp chuỗi CancellationToken thông qua mọi chức năng hoạt động với Task. Tại sao nhóm C# chọn để bọc tính toán trong đơn Task, nhưng không phải là CancellationToken?Tại sao không phải là một CancellationToken được bao gồm trong Task <T> monad?

+2

Trong C#, bạn có * "có thể bắt đầu sau, có thể đang chạy" tính toán, cùng với tùy chọn CancellationToken * dưới dạng 'var task = new Task (action, token)'. – Noseratio

Trả lời

9

Ít hoặc nhiều, chúng đóng gói việc sử dụng ngầm của CancellationToken cho phương pháp C# async. Xem xét việc này:

var cts = new CancellationTokenSource(); 
cts.Cancel(); 
var token = cts.token; 

var task1 = new Task(() => token.ThrowIfCancellationRequested()); 
task1.Start(); 
task1.Wait(); // task in Faulted state 

var task2 = new Task(() => token.ThrowIfCancellationRequested(), token); 
task2.Start(); 
task2.Wait(); // task in Cancelled state 

var task3 = (new Func<Task>(async() => token.ThrowIfCancellationRequested()))(); 
task3.Wait(); // task in Cancelled state 

Đối với một tổ chức phi async lambda, tôi đã liên kết một cách rõ ràng token với task2 để hủy bỏ để tuyên truyền một cách chính xác, bằng cách cung cấp nó như là một cuộc tranh cãi để new Task() (hoặc Task.Run). Đối với số điện thoại async lambda được sử dụng với task3, nó sẽ tự động diễn ra như một phần của async/await mã cơ sở hạ tầng.

Hơn nữa, bất kỳ token sẽ tuyên truyền hủy cho một phương pháp async, trong khi đối với phi async tính toán new Task()/Task.Run lambda nó phải là cùng thẻ truyền cho constructor nhiệm vụ hoặc Task.Run.

Tất nhiên, chúng tôi vẫn phải gọi token.ThrowIfCancellationRequested() theo cách thủ công để triển khai mẫu hủy hợp tác. Tôi không thể trả lời tại sao các đội C# và TPL quyết định thực hiện nó theo cách này, nhưng tôi đoán họ nhắm đến việc không làm phức tạp cú pháp của async/await nhưng vẫn đủ linh hoạt.

Với F #, tôi chưa xem mã IL được tạo của luồng công việc không đồng bộ, được minh họa trong số blog post của Tomas Petricek mà bạn đã liên kết. Tuy nhiên, theo như tôi hiểu, mã thông báo chỉ được kiểm tra tự động tại các vị trí nhất định của luồng công việc, tương ứng với await trong C# (tương tự, chúng tôi có thể gọi token.ThrowIfCancellationRequested() theo cách thủ công sau mỗi await trong C#). Điều này có nghĩa là bất kỳ công việc nào bị ràng buộc CPU vẫn sẽ không bị hủy ngay lập tức. Nếu không, F # sẽ phải phát ra token.ThrowIfCancellationRequested() sau mỗi lệnh IL, đây sẽ là một chi phí đáng kể.

+0

Khi nào và tại sao bạn muốn sử dụng async/await cho CPU-bound work? – GregC

+1

@GregC, bất cứ khi nào tôi cần thực hiện công việc liên kết CPU trong ứng dụng giao diện người dùng: 'var pi = await Task.Run (() => CalcPi (chữ số, mã thông báo), mã thông báo)'. – Noseratio

+1

@GregC Là một dạng song song. 'await Task.WhenAll (tác vụ)'. – Aron

2

Tác vụ ban đầu được tạo để chứa các tính năng bổ sung trong lớp, nhưng sau đó đã được thay đổi để tổng hợp một đối tượng có hỗ trợ tính năng bổ sung. Tất cả trong tên hiệu suất.

http://blogs.msdn.com/b/pfxteam/archive/2011/11/10/10235962.aspx (xem "Nhiệm vụ tái cấu trúc" trong bài báo) Bài viết của Joseph E. Hoag cung cấp thông tin chi tiết về các tối ưu hóa được thực hiện trong .NET 4.5. Tôi tin rằng nó sẽ là một giá trị đọc cho bất cứ ai cố gắng để bóp 10% cuối cùng của hiệu suất ra khỏi async/chờ đợi.

Tôi giả định quy trình suy nghĩ tương tự đã được áp dụng khi quyết định cách đóng gói chức năng hủy.

Tôi không thể nói cho C# hoặc BCL nhóm, nhưng tôi cho rằng đó là một tối ưu hóa hiệu suất hoặc là chỉ có thể trong trình biên dịch F #, hoặc hiệu suất không quan trọng đối với nhóm F #. SRP, baby!

+1

Tôi đã quên mất tờ giấy Hoag đó. Dường như ổ đĩa là để giữ cho đối tượng Task càng nhỏ càng tốt, do đó không mang theo token Cancellation, mà thay vào đó mang nó xung quanh trong máy trạng thái được tạo ra bởi async/await. –

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