2012-04-06 35 views
6

Tôi đang thử CTP không đồng bộ, phiên bản 4.5 cho phép sử dụng các phương thức không đồng bộ mà không cần phải viết phương thức Begin/End.Khi phương thức trả về void, giống như một nhiệm vụ?

Đầu dò đầu tiên của tôi là thực hiện một phương thức không đồng bộ trả về khoảng trống. Tôi thấy một vài ví dụ và thực hiện như sau:

private void btnAsync01_Click(object sender, RoutedEventArgs e) 
{ 
    UpdateTxtLog("click button: " + System.DateTime.Now); 
    method01Async(); 
    UpdateTxtLog("after ethod01Async: " + System.DateTime.Now); 
} 

private async void method01Async() 
{ 
    await TaskEx.Run(() => 
    { 
     UpdateTxtLog("Enter method01Async: " + System.DateTime.Now); 
     Thread.Sleep(10000); 
     UpdateTxtLog("exit method01Async: " + System.DateTime.Now); 
    }); 
} 

Trong dự án WPF của tôi, tôi có một hộp văn bản để xem kết quả và nút thực hiện phương pháp không đồng bộ.

Trong phương pháp async, tôi sử dụng đang chờ, đó là cần bởi vì phương pháp này là không đồng bộ và TasEx.Run để tạo một chuỗi mới trong đó thực thi mã.

Nghi ngờ của tôi là ở thời điểm này. Trong một vài ví dụ mà tôi thấy làm thế nào để tạo ra một phương pháp async trả về void, sử dụng cách này, Task.Run hoặc TaskEx.Run.

Nếu tôi không sai, Task.Run tạo một chủ đề mới để thực hiện phương pháp. Sau đó, tại sao sử dụng một phương pháp async, nếu với Task, tạo một chủ đề mới, tôi nhận được những gì tôi muốn, không để chặn các chủ đề chính?

Ngoài ra, nếu phương pháp async truy cập vào một số biến được chia sẻ, tôi phải cẩn thận với đồng thời, phải không? Vì vậy, tôi không biết lợi thế của việc sử dụng các phương thức async, ít nhất là trong trường hợp này.

Trong thực tế, tôi sử dụng cùng một mã mà không cần đồng bộ và không chờ đợi và kết quả là như nhau, chương trình chính không chặn và tất cả hoạt động như tôi mong đợi. Phương pháp này là:

private void method01Async() 
{ 
    TaskEx.Run(() => 
    { 
     UpdateTxtLog("Enter method01Async: " + System.DateTime.Now); 
     Thread.Sleep(10000); 
     UpdateTxtLog("Exit method01Async: " + System.DateTime.Now); 
    }); 
} 

Câu hỏi của tôi là, đây là cách chính xác để sử dụng async khi phương thức trả về void?

+0

BTW, The Visual Studio 11 Beta là bây giờ ra, trong đó có các tính năng từ Async CTP, với một số cải tiến và sửa lỗi. – svick

+0

Và đó là lợi ích os bằng cách sử dụng phương pháp async trong trường hợp này, nếu trong phương pháp tôi đang sử dụng một nhiệm vụ, và nhiệm vụ sử dụng một sợi niew? Nếu tôi sử dụng trực tiếp một tác vụ, mà không sử dụng một phương thức async, tôi sẽ nhận được hành vi. Có lẽ các phương pháp async có ý nghĩa hơn nếu phương thức trả về một giá trị và cho các phương thức void thì tốt hơn nên sử dụng trực tiếp một tác vụ? –

+0

Tôi khuyên tất cả các phương thức 'async' trả về' Task' hoặc 'Task ' trừ khi chúng xử lý sự kiện và * có * để trả về 'void'. Điều này cho phép bạn soạn thảo chúng nếu cần và xử lý lỗi cũng sạch hơn (nếu một phương thức 'async void' đặt ra một ngoại lệ trong ngữ cảnh WPF, nó được gửi trực tiếp đến vòng lặp thông điệp UI, vì vậy các phương thức' async void' là * không * thực sự "lửa và quên"). –

Trả lời

3

Nếu tôi không sai, Task.Run tạo ra một chủ đề mới, nơi để thực hiện phương pháp này.

Không chính xác. Task.Run() sẽ chạy mã trên một chuỗi khác với chuỗi giao diện người dùng (ít nhất là với mặc định TaskScheduler). Nhưng nó sẽ không thực sự tạo ra một chủ đề mới trong hầu hết các trường hợp, nó sẽ sử dụng lại một chủ đề hiện có từ ThreadPool.

Sau đó, tại sao sử dụng phương pháp không đồng bộ, nếu với Tác vụ, tạo chủ đề mới, tôi nhận được những gì tôi muốn, không chặn chuỗi chính?

Mấu chốt của async, trong bối cảnh của một ứng dụng giao diện người dùng, là để có thể dễ dàng thực hiện một số mã trên thread UI sau và hoạt động không đồng bộ hoàn tất.

Vì vậy, nếu bạn đã thực hiện của bạn method01Async “awaitable”, có nghĩa là, làm cho nó trở lại một Task:

private async Task method01Async() 
{ 
    await Task.Run(/* whatever */); 
} 

Sau đó bạn có thể chờ đợi nó từ phương pháp btnAsync01_Click, nếu bạn thực hiện nó `async:

private async void btnAsync01_Click(object sender, RoutedEventArgs e) 
{ 
    UpdateTxtLog("click button: " + System.DateTime.Now); 
    await method01Async(); 
    UpdateTxtLog("after method01Async: " + System.DateTime.Now); 
} 

Bằng cách này, dòng cuối cùng của phương thức sẽ chỉ thực hiện sau khi Task trong method01Async hoàn thành việc thực thi. Và nó sẽ thực thi trên chuỗi giao diện người dùng.

Trong Net 4.0, bạn có thể đạt được hiệu quả tương tự sử dụng ContinueWith()Dispatcher.Invoke():

private void btnAsync01_Click(object sender, RoutedEventArgs e) 
{ 
    UpdateTxtLog("click button: " + System.DateTime.Now); 
    method01Async().ContinueWith(() => 
     Dispatcher.Invoke(
      new Action(() => 
       UpdateTxtLog("after method01Async: " + System.DateTime.Now))); 
} 

tôi chắc chắn rằng bạn sẽ đồng ý rằng đây là nhiều Messier và ít có thể đọc được.

Ngoài ra, nếu phương pháp async truy cập một số biến được chia sẻ, tôi phải cẩn thận với đồng thời, phải không?

Có, bạn đúng về điều đó.

Trong thực tế, tôi sử dụng cùng một mã mà không cần đồng bộ và không chờ đợi và kết quả là như nhau, chương trình chính không chặn và tất cả hoạt động như tôi mong đợi.

Kết quả chắc chắn không phải là điều tôi nghĩ mã của bạn được cho là phải làm. Dòng cuối cùng của btnAsync01_Click, sẽ thực hiện "sau khi method01Async", nhưng nó sẽ không chờ đợi cho đến khi các Task bắt đầu trong phương pháp đó kết thúc.


Như một mặt lưu ý, không có nhu cầu sử dụng async trong method01Async của bạn. Trả lại Task trực tiếp (hay không, nếu bạn muốn giữ nó void -returning), sẽ làm việc như nhau:

private Task method01Async() 
{ 
    return Task.Run(/* whatever */); 
} 
+0

Vâng, khi tôi nói rằng nhiệm vụ tạo ra một chủ đề mới, thực sự tôi đã đơn giản hóa, tôi biết rằng nhiệm vụ sử dụng một chủ đề hiện có từ threadpool, và nếu có ai miễn phí, sau đó nhiệm vụ phải chờ đợi. Trong trường hợp này, tôi không muốn chờ trong phương thức chính (nhấn vào sự kiện trong trường hợp này) vì phương thức thứ cấp (method01Async) chỉ làm một cái gì đó không được thông báo cho phương thức chính. Ví dụ, gửi một email đến một cái gì đó. Tôi chỉ muốn gửi, nhưng không được chăm sóc hoặc thông báo trong đơn đăng ký chính nếu hoạt động được thực hiện hay không. –

+0

Là những gì tôi đang suy nghĩ, trong phương pháp void, có lẽ async là không cần thiết, và các phương pháp async được khuyến khích cho các phương thức trả về một giá trị. –

+0

Nếu bạn không muốn làm bất cứ điều gì sau khi các hoạt động hoàn thành, sau đó có, không có lý do gì để sử dụng 'await'. Mặc dù nó có thể có ý nghĩa để sử dụng bên trong mã gửi email, để sử dụng nhóm luồng hiệu quả hơn. – svick

1

Bạn không thực sự sử dụng async trong cả hai trường hợp, vì bạn không phải chờ cuộc gọi ban đầu. Đây là cách bạn nên làm điều đó:

private async void btnAsync01_Click(object sender, RoutedEventArgs e) 
{ 
    UpdateTxtLog("click button: " + System.DateTime.Now); 
    await method01Async(); 
    UpdateTxtLog("after ethod01Async: " + System.DateTime.Now); 
} 

private async Task method01Async() 
{ 
    return await TaskEx.Run(() => 
    { 
     UpdateTxtLog("Enter method01Async: " + System.DateTime.Now); 
     Thread.Sleep(10000); 
     UpdateTxtLog("exit method01Async: " + System.DateTime.Now); 
    }); 
} 

Khi bạn thay đổi nó thành này (một phần quan trọng là await method01Async() nút sự kiện click của bạn, nó sẽ nhảy trở lại đó sau khi nó thoát, và bạn "sau ethod01Async:" log văn bản sẽ hiển thị một giây chậm trễ mười, giống như bạn "exit method01Async" log trong phương pháp method01Async.

+0

Điều này sẽ không biên dịch. Bạn cần phải thực hiện 'btnAsync01_Click' 'async' nếu bạn muốn sử dụng' await' trong đó. – svick

+0

Có, cần phải thực hiện sự kiện nhấp chuột không đồng bộ. Nhưng với cách này, tôi có cùng một nghi ngờ, nếu trong method01Async tôi sử dụng một nhiệm vụ, tôi đang sử dụng một chủ đề mới, vì vậy tôi không thấy lợi ích của việc sử dụng async. Điều đó khiến tôi nghĩ rằng nếu các phương thức async có ý nghĩa hơn khi phương thức trả về kết quả và phương pháp void có lẽ tốt hơn nên sử dụng trực tiếp luồng bằng cách sử dụng tác vụ. –

+0

@svick là đúng. Tôi đã không thực sự biên dịch điều này vì vậy tôi đang đi từ bộ nhớ. Trình biên dịch sẽ nhanh chóng cho bạn biết rằng bạn không thể gọi 'await' từ một phương thức không được gắn thẻ với' async'. Chỉnh sửa để phản ánh điều đó. Cảm ơn svick. –

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