2015-04-23 23 views
5

Tôi có một phương pháp không đồng bộ sau khi hoàn thành mà tôi muốn chạy một phương pháp khác. Điều này hoạt động tốt nếu tôi chỉ cần gọi phương thức và thêm .ContinueWith()Tạo một nhiệm vụ lạnh có thể chờ đợi

Tuy nhiên, tôi có yêu cầu mới chỉ bắt đầu tác vụ nếu tôi có thể thêm nó vào một từ điển đồng thời.

Tôi muốn xây dựng nhiệm vụ, cố gắng để thêm nó và sau đó bắt đầu nhiệm vụ

Tuy nhiên, có vẻ như Task.Start() ngay lập tức hoàn thành nhiệm vụ gây ra tiếp tục hành động để chạy và bất cứ chờ đợi để .. không đợi.

ai cũng có thể giải thích tại sao điều này xảy ra và đúng cách để đạt được mục tiêu của tôi?

namespace UnitTestProject2 
{ 
    [TestClass] 
    public class taskProblem 
    { 
     [TestMethod] 
     public void Test() 
     { 
      CancellationTokenSource cancel = new CancellationTokenSource(); 
      ConcurrentDictionary<Guid, Task> tasks = new ConcurrentDictionary<Guid,Task>(); 
      Guid id = Guid.NewGuid(); 
      Task t = new Task(async() => await Get(), cancel.Token); 
      t.ContinueWith(Complete); 
      if (tasks.TryAdd(id, t)) 
      { 
       t.Start(); 
      } 
      else 
      { 
       //another thread is stopping stuff dont start new tasks 
      } 

      t.Wait(); //expected to wait for the get function to complete 
      Console.WriteLine("end test"); 
     } 

     public async Task Get() 
     { 
      Console.WriteLine("start task"); 
      await Task.Delay(10000); 
      Console.WriteLine("end task"); 
     } 

     public void Complete(Task t) 
     { 
      Console.WriteLine("Complete"); 
     } 
    } 
} 

đầu ra:

start task 
end test 
Complete 

đầu ra mong đợi:

start task 
end task 
Complete 
end test 

Cập nhật: Có vẻ không có cách nào để tạo một nhiệm vụ mới mà sẽ không ngay lập tức bắt đầu hoặc hoàn thành ngay lập tức trên Task.Start?

+1

Vấn đề thực sự bạn đang cố gắng giải quyết là gì?Yêu cầu làm cho không có ý nghĩa và gói Nhiệm vụ hai cấp độ sâu không giúp đỡ. Có * không bao giờ * một lý do chính đáng để tạo các nhiệm vụ lạnh và gọi 'Bắt ​​đầu'. Hơn nữa, không có lý do gì để bọc 'Get' mà đã trả về một Task đang hoạt động bên trong một cái khác. Chỉ cần viết 'Task t = Get();'. Đối với yêu cầu, nó không có ý nghĩa. Bạn luôn có thể thêm một mục mới vào một từ điển đồng thời vì bạn đang thực sự tạo một khóa Guid mới cho nó. –

+1

Dường như bạn đang truyền một đại biểu async vào hàm tạo 'Task' mà sẽ không đợi nhiệm vụ được trả về từ' Get' để hoàn tất. Tại sao bạn đang cố gắng để bọc các cuộc gọi để 'Get' trong một nhiệm vụ khác? – Lee

+0

ứng dụng thực gửi thông báo đẩy từ hàng đợi và có phương thức Stop() (được gọi từ một chuỗi khác) để ngừng bắt đầu nhiệm vụ mới. Trong trường hợp này, tôi cần tác vụ không bắt đầu để tôi có thể thêm nó vào từ điển đồng thời trong một chuỗi an toàn. rõ ràng có những cách khác để làm điều này mà tôi sẽ bị buộc phải sử dụng nếu tôi không thể hình này ra – Ewan

Trả lời

6

Đại biểu của bạn không có hiệu lực. void-phương pháp vô hiệu hóa là lửa và quên.

Xem điểm đầu tiên của Tóm tắt các Patterns và Anti-Patterns: http://rarcher.azurewebsites.net/Post/PostContent/31

lẽ bạn có thể làm điều gì đó như thế:

[TestFixture] 
public class FIXTURENAMETests { 
    [Test] 
    public async Task NAME() { 
    var tcs = new TaskCompletionSource<bool>(); 
    Task t = LongRunningStuff(tcs); 

    if (CanInsertInDictionary(t)) { 
     tcs.SetResult(true); 
    } else { 
     tcs.SetException(new Exception()); 
    } 

    Trace.WriteLine("waiting for end"); 

    try { 
     await t; 
    } 
    catch (Exception exception) { 
     Trace.WriteLine(exception); 
    } 

    Trace.WriteLine("end all"); 
    } 

    private bool CanInsertInDictionary(Task task) { 
    return true; 
    } 

    private async Task LongRunningStuff(TaskCompletionSource<bool> tcs) { 
    Trace.WriteLine("start"); 
    try { 
     await tcs.Task; 
    } 
    catch (Exception) { 
     return; 
    } 
    Trace.WriteLine("do long running stuff"); 
    await Task.Delay(10000); 
    Trace.WriteLine("end"); 
    } 
} 
+0

Ahh! tôi đã nhận ra nó phải là một cái gì đó về delon anon. nhưng bạn có giải pháp không? – Ewan

+0

tôi đã chỉnh sửa câu trả lời. có lẽ bạn có thể làm một cái gì đó như thế. –

+0

Hmmm, một công việc khác có thể xảy ra xung quanh, nhưng thực sự không có cách nào để tạo ra một nhiệm vụ không thể bắt đầu? thực sự tôi muốn làm Nhiệm vụ t = Nhận() DontStartYet() hoặc một cái gì đó – Ewan

-1

Thứ nhất, ContinueWith sẽ trả về một mới Task, bạn muốn chờ đợi cho đến khi phương thức Complete hoàn tất nhưng bạn đang đợi nhiệm vụ đầu tiên t.

Vì vậy, để đầu ra Complete trước end test, bạn phải đợi vào các nhiệm vụ thứ hai:

Task t = new Task(async() => await Get(), cancel.Token); 

// NOTE: t2 is a new Task returned from ContinueWith 
Task t2 = t.ContinueWith(Complete); 

if (tasks.TryAdd(id, t)) 
{ 
    t.Start(); 
} 
else 
{ 
} 

// NOTE: Waiting on t2, NOT t 
t2.Wait(); 

Console.WriteLine("end test"); 

Bây giờ đầu ra sẽ là:

start task 
Complete 
end test 
end task 

Ok, đây vẫn không phải là kết quả mong muốn . end task nên được in trước Complete. Điều này là do Hành động không đồng bộ của bạn không thể chờ được: How to await on async delegate

Không biết liệu tôi có hiểu chính xác yêu cầu của bạn hay không. Nếu là tôi, tôi có thể làm điều đó như thế này:

Thêm một lớp hỗ trợ mới:

public class TaskEntry 
{ 
    public Task Task { get; set; } 
} 

Sau đó thay đổi mã của bạn để:

Guid id = Guid.NewGuid(); 

Task task = null; 
var entry = new TaskEntry(); 
if (tasks.TryAdd(id, entry)) 
{ 
    entry.Task = Get(); 

    // Notice this line of code: 
    task = entry.Task.ContinueWith(Complete); 
} 

if (task != null) 
{ 
    task.Wait(); 
} 

Console.WriteLine("end test"); 

Ở đây tôi giả định TaskEntry sẽ không được được sửa đổi bởi các chủ đề khác.

+0

Bạn đúng là tôi nên chờ đợi nhiệm vụ tiếp tục để đảm bảo kết quả mong đợi của tôi. nhưng vấn đề thực sự là đại biểu void không đồng bộ. Lớp TaskEntry bổ sung của bạn là một công việc tốt xung quanh. Nhưng liệu bạn có nói KHÔNG có cách nào để tạo ra một nhiệm vụ không thể chờ đợi? – Ewan

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