2013-07-02 18 views
31

Giả sử chúng ta có phương thức liên kết I/O (chẳng hạn như phương thức tạo cuộc gọi DB). Phương thức này có thể chạy cả trong đồng bộ và không đồng bộ. Đó là,Task.Factory.StartNew vs Task.Factory.FromAsync

  1. Sync:

    IOMethod() 
    
  2. Async:

    BeginIOMethod() 
    EndIOMethod() 
    

Sau đó, khi chúng ta thực hiện phương pháp này theo nhiều cách khác nhau như hình dưới đây, sự khác biệt hiệu suất trong là những gì điều khoản sử dụng tài nguyên?

  1. var task = Task.Factory.StartNew(() => { IOMethod(); }); 
    task.Wait(); 
    
  2. var task = Task.Factory.FromAsync(BeginIOMethod, EndIOMethod, ...); 
    task.Wait(); 
    
+2

Câu trả lời ngắn gọn, bạn (có thể) không có chuỗi chủ đề nằm ở đó không làm gì khi sử dụng 'FromAsync', nếu bạn sử dụng' StartNew'. Nếu bạn đang làm rất nhiều thứ, nhấn mạnh hồ bơi luồng có thể là một vấn đề hiệu suất. – Servy

+0

Nhân bản: http://stackoverflow.com/questions/5018897/tpl-taskfactory-fromasync-vs-tasks-with-blocking-methods – shambulator

Trả lời

53
var task = Task.Factory.StartNew(() => { IOMethod(); }); 
task.Wait(); 

này sẽ chặn một thread hồ bơi trong khi IOMethod() được thực hiện và cũng chặn chủ đề hiện tại của bạn vì Wait(). Tổng số chủ đề bị chặn: 2.

var task = Task.Factory.FromAsync(BeginIOMethod, EndIOMethod, ...); 
task.Wait(); 

chí này (rất có thể) thực hiện các hoạt động không đồng bộ mà không sử dụng một chủ đề, nhưng nó sẽ chặn các thread hiện hành vì Wait(). Tổng số chủ đề bị chặn: 1.

IOMethod(); 

Điều này sẽ chặn luồng hiện tại trong khi IOMethod() đang thực thi. Tổng số chủ đề bị chặn: 1.

Nếu bạn cần chặn luồng hiện tại, hoặc nếu chặn nó là phù hợp với bạn, thì bạn nên sử dụng điều này, vì cố gắng sử dụng TPL sẽ không thực sự cung cấp cho bạn bất cứ điều gì.

var task = Task.Factory.FromAsync(BeginIOMethod, EndIOMethod, ...); 
await task; 

này sẽ thực hiện các hoạt động không đồng bộ mà không sử dụng một chủ đề, và nó cũng sẽ chờ đợi cho các hoạt động để hoàn thành đồng bộ, nhờ await. Tổng số chủ đề bị chặn: 0.

Đây là những gì bạn nên sử dụng nếu bạn muốn tận dụng lợi thế của không đồng bộ và bạn có thể sử dụng C# 5.0.

var task = Task.Factory.FromAsync(BeginIOMethod, EndIOMethod, ...); 
task.ContinueWith(() => /* rest of the method here */); 

này sẽ thực hiện các hoạt động không đồng bộ mà không sử dụng một chủ đề, và nó cũng sẽ chờ đợi cho các hoạt động để hoàn thành đồng bộ, nhờ ContinueWith(). Tổng số chủ đề bị chặn: 0.

Đây là những gì bạn nên sử dụng nếu bạn muốn tận dụng lợi thế của sự không đồng bộ và bạn không thể sử dụng C# 5.0.

+2

Tôi nghĩ rằng "task.Wait()" đã được giới thiệu chỉ vì lợi ích của ví dụ. Tôi chắc chắn rằng khi nhiệm vụ được thực hiện, OP có ý định làm một điều gì đó thêm trên đường đi. Vì vậy, tôi không nghĩ rằng bạn nên đếm thread gọi là một thread bị chặn. Nhưng nếu anh ta nghiêm túc về việc chờ đợi, thì bạn chính xác. – Tombala

+0

@Tombala Tôi không thấy bất kỳ điều gì trong câu hỏi cho biết điều đó. Nhưng đó chắc chắn là một khả năng. – svick

1

(1) sẽ (có thể) làm cho .NET bơi thread để xử lý Task của bạn.

(2) sẽ sử dụng bất kỳ cơ chế nào mà cặp BeginIOMethod/EndIOMethod của bạn tự nhiên sử dụng để xử lý phần không đồng bộ, có thể hoặc không liên quan đến nhóm chuỗi .NET.

Ví dụ: nếu BeginIOMethod gửi thông báo TCP qua internet và sau đó người nhận sẽ gửi cho bạn một thông báo TCP phản hồi (nhận được EndIOMethod), sau đó tính chất không đồng bộ của hoạt động là không được cung cấp bởi nhóm chủ đề .NET. Thư viện TCP đang được sử dụng đang cung cấp phần không đồng bộ.

Điều này có thể được thực hiện bằng cách sử dụng TaskCompletionSource class. Task.Factory.FromAsync có thể tạo TaskCompletionSource<T>, trả lại Task<T>, sau đó sử dụng EndIOMethod làm trình kích hoạt để đặt Result vào số Task<T> đã được trả lại biểu mẫu Task.Factory.FromAsync tại thời điểm gọi điện.

Sự khác biệt về hiệu suất trong việc sử dụng tài nguyên là gì?

Sự khác biệt giữa (1) và (2) chủ yếu chỉ là liệu nhóm chuỗi .NET có tải khối lượng công việc được thêm vào hay không. Nói chung, điều đúng đắn cần làm là chọn Task.Factory.FromAsync nếu bạn chỉ có một cặp Begin.../End...Task.Factory.StartNew nếu không.


Nếu bạn đang sử dụng C# 5.0, sau đó bạn sẽ sử dụng non-blocking await task; thay vì task.Wait();. (Xem trả lời svick của.)

+0

Không phải (2) vẫn sẽ phải khởi chạy một luồng để thực thi BeginIOMethod không? Nó sẽ không còn là một chủ đề trong hồ bơi thread? Theo [câu hỏi này] (http://stackoverflow.com/questions/7784589/how-does-task-factory-fromasync-work-behave), FromAsync cũng sử dụng nhóm luồng. – Tombala

+0

@Tombala 'BeginIOMethod' được chạy hoàn toàn đồng bộ trên chuỗi đang gọi' FromAsync'. --- Về câu trả lời cho câu hỏi đó, hãy xem nhận xét này: "Tác vụ kết quả sẽ theo mặc định, được thực thi trên một luồng thread" - chỉ khi thao tác thực sự yêu cầu một luồng để chạy. Ví dụ, các hoạt động I/O sẽ sử dụng một cổng hoàn thành I/O thay vì một luồng. Điều này quy mô tốt hơn như chủ đề là một nguồn lực hạn chế và hơi tốn kém. –

+0

Đúng nhưng phần còn lại của cuộc hội thoại cũng chỉ ra khả năng của những cuộc gọi lại xảy ra trên một chủ đề hồ bơi thread. Vì vậy, tùy thuộc vào việc thực hiện như thế nào Bắt đầu và Kết thúc được thực hiện, việc sử dụng có thể giống nhau, ít hơn, hoặc thậm chí nhiều hơn. Ví dụ. nếu Begin và End khởi chạy các luồng để thực hiện thao tác async của chúng thay vì các cuộc gọi lại cổng, thì có thể chúng cũng có thể thêm vào nhóm luồng. – Tombala

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