Trộn hiệu quả các phương thức đồng bộ hóa và không đồng bộ trong một phương thức?


11

Được rồi, nghe có vẻ kỳ quặc, nhưng mã rất đơn giản và giải thích tình huống tốt.

public virtual async Task RemoveFromRoleAsync(AzureTableUser user, string role)
{
    AssertNotDisposed();
    var roles = await GetRolesForUser(user);
    roles.Roles = RemoveRoles(roles.Roles, role);
    await Run(TableOperation.Replace(roles));
}

. nó cho chính xác khi xem mẫu async / await.)

Tôi bắt gặp mô hình này ngày càng thường xuyên hơn khi tôi đang sử dụng async/ awaitnhiều hơn nữa. Mẫu bao gồm chuỗi các sự kiện sau:

  1. Chờ đợi một cuộc gọi ban đầu giúp tôi có một số thông tin tôi cần để làm việc
  2. Làm việc trên thông tin đó một cách đồng bộ
  3. Chờ cuộc gọi cuối cùng để lưu công việc được cập nhật

Khối mã ở trên thường là cách tôi xử lý các phương thức này. Tôi awaitlà cuộc gọi đầu tiên, mà tôi phải thực hiện vì nó không đồng bộ. Tiếp theo, tôi thực hiện công việc mà tôi cần làm không phải là IO hoặc tài nguyên bị ràng buộc và do đó không đồng bộ. Cuối cùng, tôi lưu lại công việc của mình, đó cũng là một asynccuộc gọi, và hết việc awaitnày.

Nhưng đây có phải là cách hiệu quả / chính xác nhất để xử lý mẫu này? Dường như với tôi, tôi có thể bỏ qua awaitcuộc gọi cuối cùng, nhưng nếu nó thất bại thì sao? Và tôi có nên sử dụng một Taskphương pháp như ContinueWithđể xâu chuỗi công việc đồng bộ của mình với cuộc gọi ban đầu không? Tôi chỉ đang ở một thời điểm mà tôi không chắc liệu mình có xử lý việc này một cách chính xác hay không.

Lấy mã trong ví dụ , có cách nào tốt hơn để xử lý chuỗi cuộc gọi phương thức async / sync / async này không?


Các mã dường như đối với tôi càng ngắn và dễ hiểu càng tốt. Bất cứ điều gì tôi có thể nghĩ về việc giới thiệu sự phức tạp không cần thiết.
Euphoric

@Euphoric: Tôi có nên chờ đợi cuộc gọi cuối cùng của mình không? Điều gì sẽ xảy ra nếu tôi không và nó đã ném? Nó sẽ khác bất kỳ như hiện tại?
Tách ra

Câu trả lời:


3

Vâng, tôi nghĩ rằng đây là cách đúng đắn để làm điều đó.

Bạn không thể bỏ qua thứ hai await. Nếu bạn đã làm, phương pháp dường như hoàn thành quá sớm (trước khi việc xóa thực sự được thực hiện) và bạn sẽ không bao giờ phát hiện ra nếu việc xóa thất bại.

Tôi không thấy làm thế nào ContinueWith()hoặc bất cứ điều gì như thế sẽ giúp đỡ ở đây. Bạn có thể sử dụng nó để tránh sử dụng await, nhưng nó sẽ làm cho mã của bạn phức tạp hơn và ít đọc hơn. Và đó là toàn bộ vấn đề await: làm cho việc viết mã không đồng bộ trở nên đơn giản hơn, khi so sánh với việc sử dụng các phần tiếp theo.


0

Cách để xử lý mẫu này là đảm bảo rằng tất cả I / O không đồng bộ. Các phương thức I / O đồng bộ làm cho luồng hiện tại bị chặn trong khi nó chờ phản hồi từ đích I / O (mạng, hệ thống tệp, v.v.).

Một điều khác cần xem xét là awaitnên được sử dụng khi bạn cần một giá trị trả về hoặc khi bạn cần mã được chờ đợi để hoàn thành trước khi làm việc khác. Nếu bạn không cần một trong những điều đó, bạn có thể "bắn và quên" phương thức không đồng bộ của mình với Task.Run.

Vì vậy, để sử dụng hiệu quả nhất các tài nguyên máy tính, nếu RemoveRolescó bất kỳ I / O nào, thì nó sẽ trở thành await RemoveRolesAsyncvà các phương thức I / O được gọi bởi RemoveRolesAsynccũng nên không đồng bộ (và có thể được chờ đợi).

Nếu hiệu suất không phải là mối quan tâm lớn nhất của bạn, thì bạn nên thực hiện một số I / O đồng bộ trên một chuỗi không đồng bộ. Đó là một khoản nợ kỹ thuật mặc dù. (Trong trường hợp này, bạn có thể muốn gọi phương thức async đầu tiên bằng ConfigureAwait, tùy thuộc vào nơi mã đang chạy.)

Dưới đây là một cái nhìn sâu hơn về các thực tiễn tốt nhất - https://msdn.microsoft.com/en-us/magazine/jj991977.aspx

Dưới đây là một số lưu ý về hành vi của ConfigureAwait trong các môi trường khác nhau như ASP.NET, WebAPI, v.v. - /programming/13489065/best-practice-to-call-configureawait-for-all-server-side -code


2
Bạn không bao giờ nên bắn và quên mã với Task.Run. Tất cả các nhiệm vụ nên được chờ đợi. Nếu bạn không chờ đợi Nhiệm vụ và Nhiệm vụ được thu gom rác ở trạng thái ngoại lệ là "không quan sát được", nó sẽ khiến UnobservedTaskException được đưa ra trong thời gian chạy và có thể làm hỏng ứng dụng của bạn tùy thuộc vào phiên bản khung.
Triynko
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.