Làm mờ các dòng giữa async và các chức năng thông thường trong C # 5.0


10

Gần đây tôi dường như không thể có đủ chức năng bất ngờ async-chờ đợi mẫu của C # 5.0. Bạn đã ở đâu trong suốt cuộc đời của tôi?

Tôi hoàn toàn hồi hộp với cú pháp đơn giản, nhưng tôi gặp một khó khăn nhỏ. Vấn đề của tôi là các hàm async có một khai báo hoàn toàn khác với các hàm thông thường. Vì chỉ các chức năng async mới có thể chờ đợi trên các chức năng không đồng bộ khác, khi tôi đang cố gắng chuyển một số mã chặn cũ sang async, tôi có hiệu ứng domino của các chức năng mà tôi phải chuyển đổi.

Mọi người đã coi đây là một sự phá hoại của zombie . Khi async bị cắn trong mã của bạn, nó sẽ ngày càng lớn hơn. Quá trình chuyển không khó, chỉ cần đưa asyncvào khai báo và gói giá trị trả về Task<>. Nhưng thật khó chịu khi phải làm đi làm lại nhiều lần khi chuyển mã đồng bộ cũ.

Đối với tôi, nó sẽ tự nhiên hơn nhiều nếu cả hai loại chức năng (không đồng bộ và đồng bộ cũ đơn giản) có cùng một cú pháp. Nếu đây là trường hợp, chuyển sẽ không mất nỗ lực và tôi có thể chuyển đổi không đau đớn giữa hai hình thức.

Tôi nghĩ rằng điều này có thể làm việc nếu chúng ta tuân theo các quy tắc sau:

  1. Các chức năng Async sẽ không yêu cầu asynckhai báo nữa. Các loại trả lại của họ sẽ không phải được bọc trong Task<>. Trình biên dịch sẽ tự xác định một hàm async trong quá trình biên dịch và thực hiện tác vụ <> gói tự động khi cần.

  2. Không còn các cuộc gọi quên và quên đến các chức năng không đồng bộ. Nếu bạn muốn gọi một chức năng không đồng bộ, bạn sẽ cần chờ đợi trên đó. Tôi hầu như không sử dụng lửa và quên mọi cách, và tất cả các ví dụ về tình trạng chủng tộc điên rồ hoặc bế tắc dường như luôn dựa trên chúng. Tôi nghĩ rằng họ quá khó hiểu và "mất liên lạc" với suy nghĩ đồng bộ mà chúng ta cố gắng tận dụng.

  3. Nếu bạn thực sự không thể sống mà không có lửa và quên, sẽ có cú pháp đặc biệt cho nó. Trong mọi trường hợp, nó sẽ không phải là một phần của cú pháp hợp nhất đơn giản mà tôi đang nói đến.

  4. Từ khóa duy nhất bạn cần để biểu thị một cuộc gọi không đồng bộ là await. Nếu bạn đang chờ, cuộc gọi không đồng bộ. Nếu bạn không, cuộc gọi hoàn toàn đồng bộ cũ (hãy nhớ rằng, chúng tôi không có lửa và quên nữa).

  5. Trình biên dịch sẽ tự động xác định các chức năng không đồng bộ (vì chúng không còn khai báo đặc biệt nữa). Quy tắc 4 làm cho việc này trở nên rất đơn giản - nếu một chức năng có một awaitcuộc gọi bên trong, thì đó là async.

Điều này có thể làm việc? hoặc tôi đang thiếu một cái gì đó? Cú pháp hợp nhất này trôi chảy hơn nhiều và có thể giải quyết hoàn toàn sự phá hoại của zombie.

Vài ví dụ:

// assume this is an async function (has await calls inside)
int CalcRemoteBalanceAsync() { ... }

// assume this is a regular sync function (has no await calls inside)
int CalcRemoteBalance() { ... }

// now let's try all combinations and see what they do:

// this is the common synchronous case - it will block
int b1 = CalcRemoteBalance();

// this is the common asynchronous case - it will not block
int b2 = await CalcRemoteBalanceAsync();

// choosing to call an asynchronous function in a synchronous manner - it will block
// this syntax was used previously for async fire-and-forget, but now it's just synchronous
int b3 = CalcRemoteBalanceAsync();

// strange combination - it will block since it's calling a synchronous function
// it should probably show a compilation warning though
int b4 = await CalcRemoteBalance();

Lưu ý: đây là phần tiếp theo của một cuộc thảo luận liên quan thú vị trong SO


3
Bạn luôn chờ đợi các hoạt động không đồng bộ của bạn? Vui lòng cho tôi biết bạn không làm điều này ngay lập tức sau khi bắn chúng ...
Jimmy Hoffa

1
Ngoài ra, một trong những điều tuyệt vời về async là bạn không cần phải awaitngay lập tức. Bạn có thể làm một cái gì đó như var task = FooAsync(); Bar(); await task;. Làm thế nào tôi sẽ làm điều này trong đề xuất của bạn?
Svick

3
SO đang có một cuộc thảo luận? BFG-3000 của tôi đâu ...
Robert Harvey

2
@talkol Bạn nghĩ rằng lập trình song song là tục tĩu? Đó là một viễn cảnh thú vị, để nói rằng, ít nhất, khi bạn đang nói về async. Tôi nghĩ đó là một trong những lợi thế lớn của async- await: nó cho phép bạn dễ dàng soạn các hoạt động không đồng bộ (và không chỉ theo cách đơn giản nhất là "bắt đầu A, chờ A, bắt đầu B, chờ B"). Và đã có một cú pháp đặc biệt chính xác cho mục đích này: nó được gọi await.
Svick

1
@svick haha, bây giờ chúng tôi đã đến đó :) Tôi không nghĩ prog song song là tục tĩu, nhưng tôi nghĩ làm điều đó với async-await là. Async-await là đường cú pháp để giữ trạng thái tâm trí đồng bộ của bạn mà không phải trả giá cho việc chặn. Nếu bạn đã suy nghĩ song song, tôi sẽ khuyến khích bạn sử dụng một mô hình khác
talkol

Câu trả lời:


9

Câu hỏi của bạn đã được trả lời trong câu hỏi SO bạn liên kết.

Mục đích của async / await là giúp viết mã dễ dàng hơn trong một thế giới có nhiều hoạt động có độ trễ cao. Phần lớn các hoạt động của bạn không có độ trễ cao.

Khi WinRT lần đầu tiên ra mắt, các nhà thiết kế đã mô tả cách họ quyết định hoạt động nào sẽ không đồng bộ. Họ quyết định rằng mọi thứ sẽ mất từ ​​50ms trở lên sẽ không đồng bộ và phần còn lại của các phương thức sẽ là các phương thức thông thường, không đồng bộ.

Có bao nhiêu phương thức phải được viết lại để làm cho chúng không đồng bộ? Khoảng 10 phần trăm trong số họ. 90% khác không bị ảnh hưởng gì cả.

Eric Lippert tiếp tục giải thích chi tiết kỹ thuật khá lớn tại sao họ chọn không thực hiện phương pháp một kích cỡ phù hợp với tất cả. Về cơ bản, ông nói rằng asyncawaitlà một phần thực hiện của phong cách tiếp tục, và việc tối ưu hóa một phong cách như vậy để phù hợp với mọi trường hợp là một vấn đề khó khăn.


Xin lưu ý sự khác biệt đáng kể giữa câu hỏi SO và câu hỏi này. SO hỏi tại sao không làm mọi thứ không đồng bộ. Ở đây, chúng tôi không đề xuất rằng, chúng tôi khuyên bạn nên tạo 10% không đồng bộ, chỉ cần sử dụng cùng một cú pháp cho tất cả. Sử dụng cú pháp gần hơn có lợi thế là bạn có thể dễ dàng thay đổi 10% không đồng bộ, mà không phải chịu hiệu ứng domino từ những thay đổi này
talkol

Tôi có một chút không rõ ràng tại sao asyncsẽ tạo ra sự phá hoại zombie. Ngay cả khi một phương thức gọi 10 phương thức khác, bạn không phải chỉ cần một phương thức asynccấp cao nhất sao?
Robert Harvey

6
Giả sử 100% mã hiện tại của tôi là đồng bộ hóa. Bây giờ tôi có một hàm cấp độ lá bên trong duy nhất truy vấn DB mà tôi muốn thay đổi thành async. Bây giờ để làm cho nó không đồng bộ, tôi cần người gọi của nó là không đồng bộ, và người gọi của nó là không đồng bộ, và tiếp tục cho đến cấp cao nhất. Tất nhiên tôi đang nói về trường hợp toàn bộ chuỗi được chờ đợi (để giữ thiết kế đồng bộ của mã hoặc vượt qua các giá trị trả lại)
talkol
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.