2017-11-12 25 views
5

Tôi đang cố gắng để hiểu thêm về async/await và đặc biệt là cách trình biên dịch biết "tạm dừng" tại phương thức asyncawait mà không sinh ra các chuỗi bổ sung.Trình biên dịch C# biết khi nào cắt một phương thức async?

Như một ví dụ, giả sử tôi có một phương pháp async như

DoSomeStuff(); 
await sqlConnection.OpenAsync(); 
DoSomeOtherStuff(); 

Tôi biết rằng await sqlConnection.OpenAsync(); là nơi phương pháp của tôi bị "treo" và các chủ đề mà gọi nó trở lại hồ bơi thread và một khi Task đó là theo dõi việc mở kết nối hoàn thành sau đó một chủ đề có sẵn được tìm thấy để chạy DoSomeOtherStuff().

| DoSomeStuff() | "start" opening connection | ------------------------------------ | 
| ---------------------------------------------------------- | DoSomeOtherStuff() - | 

Đây là nơi tôi bị lẫn lộn. Tôi nhìn vào mã nguồn của OpenAsync (https://referencesource.microsoft.com/#System.Data/System/Data/Common/DBConnection.cs,e9166ee1c5d11996,references) và nó

public Task OpenAsync() { 
     return OpenAsync(CancellationToken.None); 
    } 

    public virtual Task OpenAsync(CancellationToken cancellationToken) { 
     TaskCompletionSource<object> taskCompletionSource = new TaskCompletionSource<object>(); 

     if (cancellationToken.IsCancellationRequested) { 
      taskCompletionSource.SetCanceled(); 
     } 
     else { 
      try { 
       Open(); 
       taskCompletionSource.SetResult(null); 
      } 
      catch (Exception e) { 
       taskCompletionSource.SetException(e); 
      } 
     } 

     return taskCompletionSource.Task; 
    } 

Tôi tưởng tượng để xem một số nơi trình biên dịch sẽ biết để "cắt đứt" sợi chỉ vì nhiệm vụ đã bắt đầu giao tiếp với một nguồn lực bên ngoài, nhưng Tôi không thực sự thấy rằng, và trên thực tế, các Open(); dường như ngụ ý rằng nó là đồng bộ chờ đợi. Ai đó có thể giải thích làm thế nào điều này trở thành mã "không đồng bộ đúng" không có chủ đề?

+3

Vâng, việc triển khai OpenAsync như vậy không thực sự là mã "không đồng bộ đúng". – CodeFuller

+0

Trình biên dịch C# viết lại mã của bạn, nó biến thành * hai * phương thức của một lớp ẩn. Cuộc gọi DoSomeOtherStuff() có trong phương thức thứ hai. Bất kỳ biến cục bộ nào bạn có thể có và được sử dụng trong cả hai phần sẽ trở thành các trường của lớp đó. Một cái gì đó bạn có thể nhìn thấy bằng cách sử dụng tiện ích ildasm.exe. –

+0

@CodeFuller Bạn có thể chỉ cho tôi một ví dụ về việc triển khai "không đồng bộ đúng" của OpenAsync() không? Hoặc hiển thị cách 'OpenAsync()' ở trên có thể được viết là đúng async? – Questionaire

Trả lời

5

Lợi ích của việc sử dụng kết quả async-chờ đợi từ thực tế các chủ đề đó gọi là sau

await sqlConnection.OpenAsync(); 

sẽ được phát hành và nó sẽ được cung cấp để được sử dụng bởi từ bể thread đó thuộc về. Nếu chúng ta đang nói về một ứng dụng ASP.NET, thread sẽ được giải phóng và sẽ có sẵn để phục vụ một yêu cầu HTTP đến khác. Bằng cách này, các chủ đề của nhóm luồng ASP.NET sẽ luôn sẵn sàng phục vụ các yêu cầu HTTP và sẽ không chặn ví dụ cho một I/O, như mở một kết nối đến một cơ sở dữ liệu và thực hiện một số câu lệnh SQL.

Cập nhật

Phải nói ở đây là nếu các công việc bạn sẽ await đã được hoàn thành, mã của bạn sẽ chạy đồng bộ.

+0

Đoạn thứ hai của bạn, _Regarding mở() _, là gây hiểu lầm như 'Open()' là không cần thiết để chạy trên một sợi nền. Việc thực hiện được hiển thị đồng bộ và không, thay đổi ngữ cảnh của nó. 'OpenAsync' và' Open' chạy trên cùng một ngữ cảnh và sẽ chặn một chuỗi liên kết với cuộc gọi. – JSteward

+0

@JSteward Tôi không chắc chắn rằng đối số của bạn là chính xác. Như đã nêu ở đây https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/await *** Biểu thức chờ đợi không chặn luồng mà nó đang thực hiện **. Thay vào đó, nó làm cho trình biên dịch đăng ký phần còn lại của phương thức async như là một sự tiếp nối trên nhiệm vụ được chờ đợi. Kiểm soát sau đó trở về người gọi của phương pháp async. Khi tác vụ hoàn thành, nó sẽ gọi tiếp tục của nó và việc thực thi phương thức async sẽ tiếp tục lại ở nơi nó đã dừng lại. * Toàn bộ mã trong Tác vụ được chờ đợi sẽ được chạy trong một luồng riêng biệt. – Christos

+0

Hành vi đó chỉ áp dụng nếu việc thực hiện phương thức trả về 'Task/Task ' bao giờ có năng suất và đại diện cho một phương thức không đồng bộ thực sự. Nếu việc triển khai đồng bộ và chặn; 'await' sẽ không tự động di chuyển tác phẩm sang ngữ cảnh khác. Việc triển khai chặn sẽ luôn chặn bất kể loại trả về có thể chờ đợi. – JSteward

6

Phương pháp của bạn không nhất thiết bị "treo" trên đang chờ. Nếu công việc bạn đang đợi đã được hoàn thành (trường hợp trong mã bạn đã cung cấp) - phương pháp sẽ tiếp tục như bình thường. Phương pháp bạn đang xem thực sự không phải là những gì được sử dụng bởi SqlConnection, bởi vì DbConnection là lớp cơ sở và phương thức OpenAsync là ảo. SqlConnection ghi đè và cung cấp triển khai không đồng bộ thực sự. Tuy nhiên, không phải tất cả các nhà cung cấp đều làm điều đó và những người không thực sự sẽ sử dụng triển khai mà bạn thể hiện trong câu hỏi của mình.

Khi thực hiện như vậy được sử dụng - toàn bộ điều sẽ chạy đồng bộ mà không cần bất kỳ công tắc chủ đề nào. Giả sử bạn có

public async Task Do() { 
    DoSomeStuff(); 
    await sqlConnection.OpenAsync(); 
    DoSomeOtherStuff(); 
} 

Và bạn sử dụng nhà cung cấp không cung cấp phiên bản async thực của OpenAsync. Sau đó, khi ai đó gọi tới số await Do() - chuỗi cuộc gọi sẽ thực hiện tất cả công việc (DoSomeStuff, OpenAsync, DoSomeOtherStuff). Nếu đó là giao diện người dùng - nó sẽ bị chặn trong toàn bộ thời gian (tình huống như vậy thường xảy ra khi mọi người sử dụng các phương thức "không đồng bộ" cho các nhà cung cấp đó trong chuỗi giao diện giả định bằng cách nào đó sẽ đặt công việc ra khỏi chuỗi giao diện người dùng, điều này không xảy ra).

+0

Bạn có thể chỉ cho tôi một ví dụ về việc triển khai "không đồng bộ đúng" của OpenAsync() không? – Questionaire

+0

@Questionaire đây là mã nguồn của SqlConnection: http: //referencesource.microsoft.com/#System.Data/System/Data/SqlClient/SqlConnection.cs – Evk

+0

@Questionaire async thực là phần lớn không đồng bộ IO (tệp, ổ cắm). Bạn có thể quan tâm đến việc đọc này: https://blog.stephencleary.com/2013/11/there-is-no-thread.html – Evk

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