2011-12-08 58 views
6

Mọi biến thể trên mã sau mà tôi thử không hoạt động - cho dù DoSomething() : void và được gọi là viết hoặc DoSomething() : Task và được gọi với TaskEx.RunEx(), một số nỗ lực liên quan đến .GetAwaiter().GetResult(). Lỗi được thấy bao gồm: "Start may not be called on a task with null action", "RunSynchronously may not be called on a task unbound to a delegate""The task has not yet completed".Gọi một phương thức không đồng bộ từ một phương pháp không đồng bộ

class Program 
{ 
    static void Main(string[] args) // Starting from a non-async method 
    { 
     DoSomething(); 

     Console.WriteLine("Press any key to quit."); 
     Console.ReadKey(); 
    } 

    static async void DoSomething() 
    { 
     Console.WriteLine("Starting DoSomething ..."); 

     var x = await PrepareAwaitable(1); 

     Console.WriteLine("::" + x); 

     var y = await PrepareAwaitable(2); 

     Console.WriteLine("::" + y); 
    } 

    static Task<string> PrepareAwaitable(int id) 
    { 
     return new Task<string>(() => 
     { 
      return "Howdy " + id.ToString(); 
     }); 
    } 
} 

Output:

Bắt đầu DoSomething ...

Nhấn phím bất kỳ để bỏ thuốc lá.

's sẽ phức tạp hơn sau này. Khi hành động này hoàn tất, tuy nhiên phải mất nhiều thời gian, tôi mong đợi Task (hoặc các cơ chế Khung khác) tiếp tục bằng cách gán "Howdy ..." cho x, và sau đó là y. Những gì tôi thực sự muốn làm là đánh chặn các đối tượng đang chờ đợi, xử lý chúng, và vào một thời gian sau đó tôi kiểm soát, tiếp tục với việc tiếp tục với kết quả (xy). Nhưng tôi đã không nhận được rất nhiều trên bước lớn đó, vì vậy tôi đang cố gắng để bắt đầu nhỏ hơn.

+1

Bạn đang cố * làm gì? Thật khó để biết những gì "không hoạt động" có nghĩa là khi chúng ta không biết những gì "làm việc" sẽ như thế nào. –

+0

Tôi muốn nó chạy để hoàn thành, xin vui lòng. Có lẽ nó dường như rõ ràng đối với tôi những gì nó nên làm do sự hiểu biết của tôi sai. –

+0

@uosef: Chạy cái gì? Không có gì thực sự không đồng bộ ở đây? –

Trả lời

5

Tác vụ bạn đã trả lại chưa bắt đầu (nghĩa là các tác vụ "lạnh"); hãy thử thay thế mã PrepareAwaitable bằng các thông tin sau:

static Task<string> PrepareAwaitable(int x) 
{ 
    return Task.Factory.StartNew<string>(() => 
    { 
     return "Howdy " + x.ToString(); 
    }); 
} 
+1

Hmm. Điều đó hoạt động. Làm thế nào một nhiệm vụ lạnh có thể được bắt đầu sau này? Khi tôi thử .Start() trên mã ban đầu (Nếu DoSomething(): Task) Tôi nhận được lỗi "Bắt đầu có thể không được gọi trên một nhiệm vụ với hành động null". –

5

Nó thực sự không rõ ràng những gì bạn đang cố gắng đạt được, bởi vì không có gì không đồng bộ xảy ra. Ví dụ, điều này sẽ biên dịch và chạy, nhưng tôi không biết cho dù đó là những gì bạn muốn:

using System; 
using System.Threading.Tasks; 

class Program 
{ 
    static void Main(string[] args) 
    { 
     DoSomething(); 

     Console.WriteLine("Press any key to quit."); 
     Console.ReadKey(); 
    } 

    static async void DoSomething() 
    { 
     Console.WriteLine("Starting DoSomething ..."); 

     var x = await PrepareAwaitable(1); 

     Console.WriteLine("::" + x); 

     var y = await PrepareAwaitable(2); 

     Console.WriteLine("::" + y); 
    } 

    static async Task<string> PrepareAwaitable(int x) 
    { 
     return "Howdy " + x; 
    } 
} 

Lưu ý rằng điều này mang lại một cảnh báo cho PrepareAwaitable vì không có gì không đồng bộ trong nó; không có biểu hiện "chờ đợi". Toàn bộ chương trình thực hiện đồng bộ. Một triển khai thay thế khác là PrepareAwaitable:

static Task<string> PrepareAwaitable(int x) 
{ 
    return TaskEx.Run(() => "Howdy " + x); 
} 

Điều đó giống như những gì bạn đã làm sau?

+1

Cảm ơn! TaskEx.Run() là con đường tôi đã hy vọng. Điều này có tốt hơn 'Task.Factory.StartNew ()' của CarlosFigueira không? "Tốt hơn" có nghĩa là nội tuyến với các nhà thiết kế async dự định sử dụng. Cách tiếp cận nào có ít hậu quả hạn chế hơn, hoặc chúng đồng nghĩa? –

+1

@ uosɐſ: Tôi tin rằng 'TaskEx.Run' chủ yếu chỉ là một cách viết tắt. Có thể có một số khác biệt tinh tế, nhưng nếu có thì tôi không biết chúng một cách tự nhiên. –

+0

Cảm ơn câu trả lời chi tiết! Tôi sẽ chia điểm nếu tôi có thể. –

10

Trước tiên, hãy đọc tài liệu Mẫu không đồng bộ dựa trên nhiệm vụ. Đó là dưới My Documents\Microsoft Visual Studio Async CTP\Documentation. Tài liệu này mô tả cách thiết kế API tự nhiên tiêu thụ theo await.

Thứ hai, nhận ra rằng có một số khía cạnh của lớp Task và các API có liên quan không còn thực sự áp dụng trong thế giới không đồng bộ mới. Task ban đầu được viết là cốt lõi của TPL. Nó trở thành một sự phù hợp cho lập trình không đồng bộ, vì vậy Microsoft đã sử dụng nó thay vì tạo ra một lớp "Promise" mới. Nhưng một số phương thức là các trạng thái lưu trữ, được sử dụng bởi TPL nhưng không cần thiết cho lập trình không đồng bộ.

Để trả lời câu hỏi chuẩn, trộn mã đồng bộ và không đồng bộ không phải là khá đơn giản với CTP không đồng bộ hiện tại. Tôi đã viết a library bao gồm phương thức mở rộng Task.WaitAndUnwrapException, giúp dễ dàng gọi mã không đồng bộ từ mã đồng bộ. Bạn cũng có thể quan tâm đến số AsyncContext class, điều này làm cho dấu nhắc "nhấn phím bất kỳ" không cần thiết.

+0

Đây là một câu trả lời thực sự tuyệt vời. Bạn minh họa một sự hiểu biết sâu sắc hơn về cuộc đấu tranh chung của tôi với async như tôi đang học nó, và tôi sẽ chia các điểm nếu tôi có thể. –

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