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?
Trả lời
Í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ể.
Khi nào và tại sao bạn muốn sử dụng async/await cho CPU-bound work? – GregC
@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
@GregC Là một dạng song song. 'await Task.WhenAll (tác vụ)'. – Aron
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!
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. –
- 1. Tại sao lại là MonadPlus chứ không phải Monad + Monoid?
- 2. Tại sao không phải là `tham gia` một phần của lớp` Monad`
- 3. State Monad, tại sao không phải là một bộ dữ liệu?
- 4. Tại sao Task <T> không cùng biến thể?
- 5. Tại sao các tiêu đề C++ này được chỉ định bao gồm <initializer_list>?
- 6. Tại sao không phải là <textarea> tự đóng?
- 7. Tại sao là <br> một phần tử, không phải là một thực thể?
- 8. Gradle: War Task có Xung đột Bao gồm/Loại trừ
- 9. Tại sao không ReadOnlyCollection <> bao gồm các phương thức như FindAll(), FindFirst(),
- 10. C++ - bao gồm unistd.h: tại sao không cunistd?
- 11. Tại sao không request.rawurl bao gồm phần http: // localhost?
- 12. Tại sao LINQ không bao gồm từ khóa `khác biệt`?
- 13. Yii không phải là tự động bao gồm jquery
- 14. Tại sao iostream lại bao gồm time.h?
- 15. Tại sao debug_backtrace() không bao gồm số dòng đôi khi?
- 16. C++ std :: vector <> :: iterator không phải là một con trỏ, tại sao?
- 17. Tại sao bao gồm ('php: // input') không hoạt động?
- 18. Java 8, tại sao không phải là một lớp ZonedTime?
- 19. Tại sao JavaFX API không được bao gồm trong Java 8 J2SE?
- 20. Tại sao điều này không phải là một tuyên bố
- 21. Luồng không khí: PythonOperator: tại sao bao gồm 'ds' arg?
- 22. Tại sao bạn phải bao gồm tệp PHP, khi sử dụng không gian tên?
- 23. Tại sao Control.Monad.Morph.hoist lại có ràng buộc Monad?
- 24. Tại sao Tổng và Sản phẩm không phải là Functors
- 25. Tại sao kết quả của unsigned char << unsigned char không phải là unsigned char
- 26. Tại sao phương thức đồng bộ không được bao gồm trong giao diện
- 27. Tại sao JavaFX không được bao gồm trong OpenJDK 8 trên Ubuntu Wily (15.10)?
- 28. Tại sao phương thức TaskFactory.StartNew không phải là chung?
- 29. Tại sao không phải là presentationController: viewControllerForAdaptivePresentationStyle: được gọi?
- 30. Tại sao Tuple không có cá thể Monad?
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