2012-11-26 34 views
5

Tôi muốn chuỗi Task s, sau đó bắt đầu chuỗi song song. đoạn này chỉ là để minh họa cho câu hỏi của tôi:Cách thích hợp để chuỗi Nhiệm vụ

 var taskOrig = new Task(() => { }); 
     var task = taskOrig; 
     foreach (var msg in messages) 
     { 
      task=task.ContinueWith(t => Console.WriteLine(msg)); 
     } 
     taskOrig.Start(); 

Tất cả mọi thứ hoạt động tốt, ngoại trừ một người cầu toàn nhỏ bên trong tôi không thích người khác phương pháp có sản phẩm nào được thực hiện đầu tiên () => { }.

Có cách nào để tránh không?

Tôi hiểu Nó hầu như không ảnh hưởng đến hiệu suất (trừ khi bạn thực hiện thường xuyên), nhưng vẫn còn. Các vấn đề về hiệu suất trong trường hợp của tôi, vì vậy hãy kiểm tra xem tác vụ có tồn tại trong mỗi lần lặp không phải là cách để thực hiện nó.

+1

"các vấn đề hiệu suất trong trường hợp của tôi, vì vậy kiểm tra nếu nhiệm vụ tồn tại trong mỗi lần lặp không phải là cách để làm điều đó. ": thời gian cần thiết là không đáng kể so với thực sự thực hiện nhiệm vụ. Trừ khi bạn đã thực sự đo lường một hiệu suất hit, nó rõ ràng là một trường hợp tối ưu hóa sớm. –

+0

@ThomasLevesque bạn có lẽ đúng, tôi chỉ nghĩ rằng có lẽ tôi đã bỏ lỡ một cái gì đó trong 'Task' tạo API. Tôi sẽ có cơ hội đánh giá hiệu suất sau này. – Anri

+0

Bạn có thể thấy TPL DataFlow thú vị – sll

Trả lời

3

Bạn có thể làm điều này:

Task task = Task.FromResult<object>(null); 
foreach (var msg in messages) 
{ 
    task = task.ContinueWith(t => Console.WriteLine(msg)); 
} 

Giải pháp trước đó sẽ không hoạt động trong 4.0. Trong 4.0 bạn sẽ cần phải làm như sau thay vì: (. Bạn có thể di chuyển SetResult đến trước khi vòng lặp foreach nếu bạn thích)

var tcs = new TaskCompletionSource<object>(); 
Task task = tcs.Task; 
foreach (var msg in messages) 
{ 
    task = task.ContinueWith(t => Console.WriteLine(msg)); 
} 

tcs.SetResult(null); 

Về mặt kỹ thuật nó không giống như các continuations sẽ bắt đầu thực hiện trong khi bạn vẫn đang bổ sung thêm. Đó không phải là một vấn đề mặc dù.

Một lựa chọn khác là sử dụng một cái gì đó như thế này:

public static Task ForEachAsync<T>(IEnumerable<T> items, Action<T> action) 
{ 
    return Task.Factory.StartNew(() => 
    { 
     foreach (T item in items) 
     { 
      action(item); 
     } 
    }); 
} 

Một cách sử dụng ví dụ sẽ là:

ForEachAsync(messages, msg => Console.WriteLine(msg)); 
+0

Một lưu ý: 'Task.FromResult()' là mới trong .Net 4.5, trong .Net 4.0, bạn sẽ cần phải làm điều này bằng tay bằng cách sử dụng 'TaskCompletionSource'. – svick

+0

@svick Phải, giải pháp được thêm vào. – Servy

+0

Nice, thanx, 'Task.FromResult (null)' - đây chính xác là những gì tôi đã bỏ lỡ trong thư mục Task Parallel – Anri

2

Một cách để làm điều đó, là để tạo ra nhiệm vụ trong vòng lặp nếu nó là null, nhưng mã mà bạn cung cấp sẽ tốt hơn cho tôi:

Task task = null; 
foreach (var msg in messages) 
{ 
    if (task == null) 
     task = new Task(() => Console.WriteLine(msg)) 
    else 
     task = task.ContinueWith(t => Console.WriteLine(msg)); 
} 
task.Start(); 
+0

Về mặt kỹ thuật - có, nhưng điều đó có nghĩa là tôi sẽ phải kiểm tra nó mỗi vòng lặp để nó thậm chí sẽ chậm hơn. Các vấn đề về hiệu suất, tôi nên thêm mã này vào câu hỏi – Anri

+0

mã mà bạn cung cấp thực sự tốt đẹp, nhưng hãy chờ các tùy chọn khác –

1

Có lẽ đây:

if(messages.Length > 0) 
{ 
    Task task = new Task(t => Console.WriteLine(messages[0])); 

    for(int i = 1; i < messages.Length; i++) 
    { 
     task = task.ContinueWith(t => Console.WriteLine(messages[i])); 
    } 
    task.Start(); 
} 
+0

Đó là tùy chọn hiệu quả nhất, nhưng ít đọc được hơn ... –

+0

Cảm ơn, đó là một lựa chọn nhưng sẽ không hoạt động. đôi khi tất cả những gì bạn có là điều tra viên, và các nhiệm vụ cũng có thể được thêm vào theo những cách khác. – Anri

+0

@Anri Bạn cũng có thể làm điều tương tự với điều tra viên (bằng cách truy cập bằng tay 'MoveNext()' và 'Current'), ngoại trừ nó sẽ còn nhiều hơn mã và thậm chí còn lộn xộn hơn. – svick

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