Cách thức và thời điểm sử dụng 'async' và 'await'


1065

Theo hiểu biết của tôi, một trong những điều chính asyncawait phải làm là làm cho mã dễ viết và đọc - nhưng liệu sử dụng chúng có giống như sinh ra các luồng nền để thực hiện logic thời lượng dài không?

Tôi hiện đang thử ví dụ cơ bản nhất. Tôi đã thêm một số ý kiến ​​nội tuyến. Bạn có thể làm rõ nó cho tôi?

// I don't understand why this method must be marked as `async`.
private async void button1_Click(object sender, EventArgs e)
{
    Task<int> access = DoSomethingAsync();
    // task independent stuff here

    // this line is reached after the 5 seconds sleep from 
    // DoSomethingAsync() method. Shouldn't it be reached immediately? 
    int a = 1; 

    // from my understanding the waiting should be done here.
    int x = await access; 
}

async Task<int> DoSomethingAsync()
{
    // is this executed on a background thread?
    System.Threading.Thread.Sleep(5000);
    return 1;
}

48
Ngoài ra, trong ví dụ của bạn thông báo rằng bạn nhận được cảnh báo khi bạn biên dịch mã ở trên. Hãy chú ý đến cảnh báo . Nó nói với bạn rằng mã này không có ý nghĩa.
Eric Lippert

Câu trả lời:


759

Khi sử dụng asyncawaittrình biên dịch tạo ra một máy trạng thái trong nền.

Đây là một ví dụ mà tôi hy vọng tôi có thể giải thích một số chi tiết cấp cao đang diễn ra:

public async Task MyMethodAsync()
{
    Task<int> longRunningTask = LongRunningOperationAsync();
    // independent work which doesn't need the result of LongRunningOperationAsync can be done here

    //and now we call await on the task 
    int result = await longRunningTask;
    //use the result 
    Console.WriteLine(result);
}

public async Task<int> LongRunningOperationAsync() // assume we return an int from this long running operation 
{
    await Task.Delay(1000); // 1 second delay
    return 1;
}

OK, vậy chuyện gì xảy ra ở đây:

  1. Task<int> longRunningTask = LongRunningOperationAsync(); bắt đầu thực hiện LongRunningOperation

  2. Công việc độc lập được thực hiện trên giả sử Chủ đề chính (ID chủ đề = 1) sau đó await longRunningTaskđạt được.

    Bây giờ, nếu longRunningTaskchưa kết thúc và nó vẫn đang chạy, MyMethodAsync()sẽ quay lại phương thức gọi của nó, do đó luồng chính không bị chặn. Khi longRunningTaskhoàn thành thì một luồng từ ThreadPool (có thể là bất kỳ luồng nào) sẽ quay trở lại MyMethodAsync()trong ngữ cảnh trước đó và tiếp tục thực thi (trong trường hợp này là in kết quả ra bàn điều khiển).

Trường hợp thứ hai sẽ là việc longRunningTaskđã hoàn thành việc thực hiện và kết quả là có sẵn. Khi đạt đến await longRunningTaskkết quả chúng ta đã có kết quả nên mã sẽ tiếp tục thực thi trên cùng một luồng. (trong trường hợp này kết quả in ra bàn điều khiển). Tất nhiên đây không phải là trường hợp của ví dụ trên, nơi có Task.Delay(1000)liên quan.


65
Tại sao chúng ta có một "sự chờ đợi" với "Nhiệm vụ ngày mai (1000);" trong phương pháp async LongRastyOperation?
Benison Sam

3
@codea Trong các bình luận của Eric Lippert cho bài viết , ông đã liên kết một bài viết giới thiệu với chủ đề này, nơi ông so sánh cụ thể chiến lược DoEvents với async-await
Camilo Martinez

13
Chủ đề @BenisonSam hơi cũ, nhưng tôi đã có cùng một câu hỏi và đang tìm kiếm một câu trả lời. Lý do cho "sự chờ đợi" là nếu chúng ta bỏ qua "sự chờ đợi" thì LongRastyOperationAsync () sẽ quay lại ngay lập tức. Trong thực tế, trình biên dịch sẽ đưa ra một cảnh báo nếu chúng ta loại bỏ sự chờ đợi. Blog của Stephen Cleary đăng bài blog.stephencleary.com/2011/09/ cho một người thực hiện các cuộc thảo luận thiết kế.
Shelbypereira 4/12/2015

70
Nếu mọi phương thức async cần có một sự chờ đợi bên trong nó và việc chờ đợi chỉ có thể được thực hiện trên một phương thức với async, khi nào nó dừng lại?
Bruno Santos

108
Câu trả lời này rõ ràng là sai. Những upvote này sẽ gây ra hiểu sai cho nhiều người dùng. Tài liệu MS nói rõ, không có chủ đề nào khác được sử dụng khi chỉ sử dụng async, đang chờ. msdn.microsoft.com/en-us/l Library / mt674882.aspx Xin ai đó sửa câu trả lời. Do đó, tôi đã lãng phí cả một ngày.
Krishna Deepak

171

Theo hiểu biết của tôi, một trong những điều chính mà async và chờ đợi làm là làm cho mã dễ viết và đọc.

Có để làm cho mã không đồng bộ dễ viết và đọc, vâng.

Đây có phải là điều tương tự như sinh ra các chủ đề nền để thực hiện logic thời gian dài?

Không có gì.

// Tôi không hiểu tại sao phương thức này phải được đánh dấu là 'async'.

Các asynctừ khóa cho phép awaittừ khóa. Vì vậy, bất kỳ phương pháp sử dụng awaitphải được đánh dấu async.

// Dòng này đạt được sau 5 giây ngủ từ phương thức DoS SomethingAsync (). Không nên đạt được ngay lập tức?

Không, bởi vì asynccác phương thức không được chạy trên một luồng khác theo mặc định.

// Điều này có được thực hiện trên một luồng nền không?

Không.


Bạn có thể tìm thấy async/ awaitgiới thiệu của tôi hữu ích. Các tài liệu MSDN chính thức cũng tốt một cách bất thường (đặc biệt là phần TAP ) và asyncnhóm đã đưa ra một Câu hỏi thường gặp xuất sắc .


6
Vì vậy, nó không chạy trên một chủ đề nền, nhưng nó cũng không chặn. Điều này có thể do API không đồng bộ sử dụng các hàm gọi lại thay vì tung hứng với các luồng. Bạn bắt đầu một hoạt động (I / O, socket, ..) và quay lại làm việc của mình. Khi hoạt động xong, HĐH sẽ gọi lại cuộc gọi lại. Đây là những gì Node.js hoặc khung công tác Python Twisted làm và họ cũng có một số lời giải thích hay.
Roman Plášil

3
"Từ khóa async cho phép từ khóa await. Vì vậy, bất kỳ phương pháp nào sử dụng await phải được đánh dấu là async.", - nhưng tại sao? câu trả lời này không giúp hiểu lý do tại sao phương thức phải được đánh dấu là không đồng bộ. Trình biên dịch có thể suy ra rằng phương thức này không đồng bộ bằng cách nhìn vào bên trong để chờ từ khóa không?
Stanislav

9
@Stanislav: Tôi có một mục blog giải quyết câu hỏi đó.
Stephen Cleary

3
Làm rõ đề xuất: Không, bởi vì asynccác phương thức không được chạy trên một luồng khác theo mặc định. Trong ví dụ của bạn, Sleep()cuộc gọi trong DoSomethingAsync()khối chặn luồng hiện tại sẽ ngăn việc thực thi tiếp tục bên trong button1_Click()cho đến khi DoSomethingAsync()hoàn thành. Lưu ý rằng trong khi Thread.Sleep()chặn luồng thực thi,Task.Delay() does not.
DavidRR

166

Giải trình

Dưới đây là một ví dụ nhanh về async/ awaitở mức cao. Có rất nhiều chi tiết để xem xét ngoài này.

Lưu ý: Task.Delay(1000)mô phỏng làm việc trong 1 giây. Tôi nghĩ tốt nhất nên nghĩ về điều này như chờ đợi phản hồi từ nguồn lực bên ngoài. Vì mã của chúng tôi đang chờ phản hồi, hệ thống có thể đặt tác vụ đang chạy sang một bên và quay lại với nó sau khi hoàn thành. Trong khi đó, nó có thể làm một số công việc khác trên chủ đề đó.

Trong ví dụ dưới đây, khối đầu tiên đang thực hiện chính xác điều đó. Nó bắt đầu tất cả các nhiệm vụ ngay lập tức (các Task.Delaydòng) và đặt chúng sang một bên. Mã sẽ tạm dừng trên await adòng cho đến khi độ trễ 1 giây được thực hiện trước khi đi đến dòng tiếp theo. Kể từ khi b, c, d, và etất cả bắt đầu thực hiện tại gần như cùng một thời điểm chính xác như a(do thiếu sự chờ đợi), họ sẽ kết thúc ở khoảng cùng thời gian trong trường hợp này.

Trong ví dụ dưới đây, khối thứ hai đang bắt đầu một nhiệm vụ và chờ đợi nó kết thúc (đó là những gì awaitlàm) trước khi bắt đầu các nhiệm vụ tiếp theo. Mỗi lần lặp này mất 1 giây. Việc awaitnày đang tạm dừng chương trình và chờ kết quả trước khi tiếp tục. Đây là sự khác biệt chính giữa khối thứ nhất và thứ hai.

Thí dụ

Console.WriteLine(DateTime.Now);

// This block takes 1 second to run because all
// 5 tasks are running simultaneously
{
    var a = Task.Delay(1000);
    var b = Task.Delay(1000);
    var c = Task.Delay(1000);
    var d = Task.Delay(1000);
    var e = Task.Delay(1000);

    await a;
    await b;
    await c;
    await d;
    await e;
}

Console.WriteLine(DateTime.Now);

// This block takes 5 seconds to run because each "await"
// pauses the code until the task finishes
{
    await Task.Delay(1000);
    await Task.Delay(1000);
    await Task.Delay(1000);
    await Task.Delay(1000);
    await Task.Delay(1000);
}
Console.WriteLine(DateTime.Now);

ĐẦU RA:

5/24/2017 2:22:50 PM
5/24/2017 2:22:51 PM (First block took 1 second)
5/24/2017 2:22:56 PM (Second block took 5 seconds)

Thông tin thêm về Đồng bộ hóa Nội dung

Lưu ý: Đây là nơi mọi thứ có một chút sương mù đối với tôi, vì vậy nếu tôi sai về bất cứ điều gì, xin vui lòng sửa cho tôi và tôi sẽ cập nhật câu trả lời. Điều quan trọng là phải có hiểu biết cơ bản về cách thức hoạt động của nó nhưng bạn có thể nhận được mà không cần phải là chuyên gia về nó miễn là bạn không bao giờ sử dụng ConfigureAwait(false), mặc dù bạn có thể sẽ mất cơ hội tối ưu hóa, tôi giả sử.

Có một khía cạnh của điều này làm cho khái niệm async/ awaithơi khó nắm bắt hơn. Đó là thực tế là trong ví dụ này, tất cả điều này xảy ra trên cùng một luồng (hoặc ít nhất là những gì dường như là cùng một luồng liên quan đến nó SynchronizationContext). Theo mặc định, awaitsẽ khôi phục bối cảnh đồng bộ hóa của luồng gốc mà nó đang chạy. Ví dụ: trong ASP.NET bạn có một chuỗi HttpContextđược gắn với một luồng khi có yêu cầu. Ngữ cảnh này chứa những thứ cụ thể cho yêu cầu http ban đầu, chẳng hạn như đối tượng Yêu cầu ban đầu có những thứ như ngôn ngữ, địa chỉ IP, tiêu đề, v.v. Nếu bạn chuyển đổi chủ đề giữa chừng khi xử lý một cái gì đó, bạn có khả năng sẽ cố gắng kéo thông tin ra khỏi đối tượng này trên một đối tượng khácHttpContextđó có thể là thảm họa. Nếu bạn biết bạn sẽ không sử dụng bối cảnh cho bất cứ điều gì, bạn có thể chọn "không quan tâm" đến nó. Điều này về cơ bản cho phép mã của bạn chạy trên một luồng riêng biệt mà không mang bối cảnh xung quanh với nó.

Làm thế nào để bạn đạt được điều này? Theo mặc định, await a;mã thực sự đang đưa ra một giả định rằng bạn KHÔNG muốn nắm bắt và khôi phục bối cảnh:

await a; //Same as the line below
await a.ConfigureAwait(true);

Nếu bạn muốn cho phép mã chính tiếp tục trên một luồng mới mà không có ngữ cảnh gốc, bạn chỉ cần sử dụng false thay vì true để nó biết rằng nó không cần khôi phục lại bối cảnh.

await a.ConfigureAwait(false);

Sau khi chương trình hoàn thành bị tạm dừng, nó sẽ tiếp tục tiềm năng trên một luồng hoàn toàn khác với bối cảnh khác. Đây là nơi cải thiện hiệu suất sẽ đến từ - nó có thể tiếp tục trên bất kỳ chuỗi có sẵn nào mà không phải khôi phục bối cảnh ban đầu mà nó bắt đầu.

Là thứ này khó hiểu? Chúa ơi Bạn có thể hình dung về nó? Có lẽ! Khi bạn đã nắm bắt được các khái niệm, sau đó chuyển sang các giải thích của Stephen Cleary có xu hướng hướng đến một người có hiểu biết về kỹ thuật về async/ awaitđã có.


Hãy nói nếu tất cả các tác vụ này đang trả về một int và nếu tôi đang sử dụng kết quả của nhiệm vụ đầu tiên trong nhiệm vụ thứ hai (hoặc một số tính toán) thì có sai không?
veerendra gupta

3
@veerendragupta vâng. Bạn có ý thức chọn không chạy chúng không đồng bộ trong trường hợp đó (vì chúng không đồng bộ). Ngoài ra còn có một vài điều khác để nhận ra về bối cảnh cấu hình mà tôi sẽ không đi vào đây
Joe Phillips

Vậy await MethodCall()là một sự lãng phí tuyệt đối? Bạn cũng có thể thả await/ async?
Vitani

2
@Jocie Không hẳn. Khi bạn gọi await, tôi nghĩ rằng nó sẽ giải phóng chuỗi trở lại hồ bơi thay vì giữ nó. Điều này làm cho nó có sẵn để sử dụng ở nơi khác trong khi chờ đợi sự trở lại của Nhiệm vụ
Joe Phillips

2
@JoePhillips Tôi nghĩ những gì bạn vừa nói là bản chất của async / await. Chuỗi cuộc gọi được giải phóng và có thể được sử dụng bởi các quy trình khác trên máy. Khi cuộc gọi chờ hoàn tất, một luồng mới được sử dụng để tiếp tục những gì người gọi ban đầu bắt đầu. Người gọi vẫn đang chờ, nhưng lợi ích là một chuỗi được giải phóng trong lúc này. Đó là lợi ích của async / chờ đợi?
Bob Horn

147

Hơn nữa với các câu trả lời khác, hãy xem chờ đợi (Tham khảo C #)

và cụ thể hơn ở ví dụ bao gồm, nó giải thích tình huống của bạn một chút

Ví dụ về Windows Forms sau đây minh họa việc sử dụng await trong một phương thức async, WaitAsyn syncinglyAsync. Đối chiếu hành vi của phương thức đó với hành vi của WaitSynync. Không có toán tử chờ được áp dụng cho một tác vụ, WaitSynync chạy đồng bộ mặc dù sử dụng công cụ sửa đổi async trong định nghĩa của nó và gọi đến Thread.S ngủ trong cơ thể của nó.

private async void button1_Click(object sender, EventArgs e)
{
    // Call the method that runs asynchronously.
    string result = await WaitAsynchronouslyAsync();

    // Call the method that runs synchronously.
    //string result = await WaitSynchronously ();

    // Display the result.
    textBox1.Text += result;
}

// The following method runs asynchronously. The UI thread is not
// blocked during the delay. You can move or resize the Form1 window 
// while Task.Delay is running.
public async Task<string> WaitAsynchronouslyAsync()
{
    await Task.Delay(10000);
    return "Finished";
}

// The following method runs synchronously, despite the use of async.
// You cannot move or resize the Form1 window while Thread.Sleep
// is running because the UI thread is blocked.
public async Task<string> WaitSynchronously()
{
    // Add a using directive for System.Threading.
    Thread.Sleep(10000);
    return "Finished";
}

3
cảm ơn câu trả lời Nhưng WaitAsyn syncinglyAsync () được thực thi trên một luồng riêng biệt?
Dan Dinu

32
Tôi tin tưởng như vậy, từ phần Một biểu thức chờ đợi không chặn luồng mà nó đang thực thi. 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ư một phần tiếp theo của tác vụ được chờ đợi. Điều khiển sau đó trả về cho người gọi phương thức async. Khi tác vụ hoàn thành, nó gọi tiếp tục và thực thi phương thức async tiếp tục ở nơi nó dừng lại.
Stander Adriaan

13
Theo bài viết MSDN này , "Không đồng bộ và chờ đợi từ khóa không tạo ra các luồng bổ sung .... một phương thức async không chạy trên luồng của chính nó". Sự hiểu biết của tôi là khi chờ từ khóa, khung bỏ qua phía trước (quay lại người gọi) để cho phép tất cả các mã độc lập có thể chạy trong khi chờ các hoạt động dài kết thúc. Tôi nghĩ điều đó có nghĩa là một khi tất cả các mã độc lập đã chạy, nếu hoạt động dài không quay trở lại, nó sẽ bị chặn. Tôi chỉ đang học điều này bây giờ, mặc dù.
Vimes

9
@astander Điều đó không chính xác. Nó không thực thi trên một chủ đề khác. Nó chỉ lập lịch trình tiếp tục (phần còn lại của phương thức) được gọi khi bộ định thời được sử dụng bởi các Task.Delayđám cháy.
MgSam

1
Câu trả lời này là sai vì Ngủ. Xem câu trả lời được chấp nhận khi chờ đợi Task.Delay (1000); trong đó có hành vi đúng.
Jared Updike

62

Hiển thị các giải thích trên trong hành động trong một chương trình điều khiển đơn giản:

class Program
{
    static void Main(string[] args)
    {
        TestAsyncAwaitMethods();
        Console.WriteLine("Press any key to exit...");
        Console.ReadLine();
    }

    public async static void TestAsyncAwaitMethods()
    {
        await LongRunningMethod();
    }

    public static async Task<int> LongRunningMethod()
    {
        Console.WriteLine("Starting Long Running method...");
        await Task.Delay(5000);
        Console.WriteLine("End Long Running method...");
        return 1;
    }
}

Và đầu ra là:

Starting Long Running method...
Press any key to exit...
End Long Running method...

Như vậy

  1. Main bắt đầu phương thức chạy dài qua TestAsyncAwaitMethods. Điều đó ngay lập tức trở lại mà không tạm dừng chuỗi hiện tại và chúng tôi ngay lập tức thấy thông báo 'Nhấn phím bất kỳ để thoát'
  2. Tất cả điều này trong khi, LongRunningMethodđang chạy trong nền. Sau khi hoàn thành, một luồng khác từ Threadpool chọn bối cảnh này và hiển thị thông báo cuối cùng

Do đó, không có chủ đề bị chặn.


"Nhấn phím bất kỳ để thoát ..." sẽ được hiển thị ở phần nào của đầu ra?
StudioX

1
và việc sử dụng (trở lại 1) là gì? có cần thiết không
StudioX

1
@StudioX Tôi nghĩ rằng nó phải có số nguyên kiểu trả về
Kuba Do

Tôi nghĩ rằng return 1phần này xứng đáng được giải thích thêm: awaittừ khóa cho phép bạn trả lại loại cơ bản Task<T>trực tiếp, do đó giúp dễ dàng điều chỉnh mã thoát của bạn sang thế giới await / async . Nhưng bạn không phải trả về một giá trị, vì có thể trả về một giá trị Taskmà không chỉ định loại trả về, tương đương với một voidphương thức đồng bộ . Lưu ý rằng C # cho phép async voidcác phương thức, nhưng bạn nên tránh làm như vậy trừ khi bạn xử lý các trình xử lý sự kiện.
Christiano Kiss

41

Tôi nghĩ bạn đã chọn một ví dụ tồi với System.Threading.Thread.Sleep

Điểm của một asyncnhiệm vụ là để cho nó thực thi trong nền mà không khóa luồng chính, chẳng hạn như thực hiện mộtDownloadFileAsync

System.Threading.Thread.Sleep không phải là một cái gì đó "đang được thực hiện", nó chỉ ngủ, và do đó dòng tiếp theo của bạn đạt được sau 5 giây ...

Đọc bài viết này, tôi nghĩ rằng đó là một lời giải thích tuyệt vời asyncawaitkhái niệm: http://msdn.microsoft.com/en-us/l Library / vstudio / hh191443.aspx


3
Tại sao Ngủ là ví dụ xấu nhưng Tải xuống là ví dụ tốt. Nó giống như kiểu FooBar khi tôi thấy Thread. Ngủ tôi hiểu rằng có một số nhiệm vụ phải mất nhiều thời gian. Tôi nghĩ rằng câu hỏi của anh ấy có liên quan
Abdurrahim

1
@Abdurrahim Thread.Sleepchặn luồng (luồng không thể làm gì khác ngoài trạng thái chờ), nhưng phương thức async thì không. Trong trường hợp DownloadFileAsync, luồng có thể đi và làm một cái gì đó khác cho đến khi một câu trả lời đến từ máy chủ từ xa. Một trình giữ chỗ tốt hơn cho "một số tác vụ cần có thời gian" trong phương thức Task.Delaykhông đồng bộ là vì điều đó thực sự không đồng bộ.
Gabriel Luci

@GabrielLuci sự phản đối của tôi không phải là về Trì hoãn vs Ngủ; Câu trả lời của bạn trông giống như một câu trả lời của người rơm; Nếu bạn đặt câu hỏi này như một bình luận cho câu hỏi sẽ không có gì tôi có thể phản đối, nhưng như một câu trả lời, nó có mùi giống như một câu trả lời của người rơm. Tôi nghĩ vẫn ổn khi sử dụng async, thậm chí tất cả các cuộc gọi mà anh ấy / cô ấy phải thực hiện sẽ chặn các cuộc gọi; Nó sẽ không làm mất hiệu lực tất cả các mục đích ... Thậm chí tất cả những gì còn lại sẽ là đường cú pháp, nó được tính là một trường hợp hợp lệ,
Abdurrahim

1
Đây không phải là câu trả lời của tôi. Nhưng để giải quyết quan điểm của bạn: nó phụ thuộc vào mục đích của phương pháp. Nếu anh ta chỉ muốn một phương thức để gọi, anh ta đã thành công. Nhưng trong trường hợp này, anh ta đã cố gắng tạo ra một phương thức chạy không đồng bộ. Ông đã làm điều đó bằng cách chỉ sử dụng asynctừ khóa. Nhưng phương pháp của anh ta vẫn chạy đồng bộ và câu trả lời này đã giải thích hoàn hảo tại sao: bởi vì anh ta thực sự không chạy bất kỳ mã không đồng bộ nào. Các phương thức được đánh dấu asyncvẫn chạy đồng bộ cho đến khi bạn awaitchưa hoàn thành Task. Nếu không có await, thì phương thức sẽ chạy đồng bộ và trình biên dịch sẽ cảnh báo bạn về điều đó.
Gabriel Luci

23

Đây là một chương trình điều khiển nhanh để làm cho nó rõ ràng cho những người theo dõi. Các TaskToDophương pháp là phương pháp chạy dài của bạn mà bạn muốn làm cho async. Làm cho nó chạy async được thực hiện bằng TestAsyncphương pháp. Phương thức vòng lặp kiểm tra chỉ chạy qua các TaskToDotác vụ và chạy chúng không đồng bộ. Bạn có thể thấy điều đó trong kết quả vì chúng không hoàn thành theo cùng một thứ tự từ chạy đến chạy - chúng đang báo cáo cho luồng UI giao diện điều khiển khi chúng hoàn thành. Đơn giản, nhưng tôi nghĩ các ví dụ đơn giản đưa ra cốt lõi của mẫu tốt hơn các ví dụ liên quan nhiều hơn:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace TestingAsync
{
    class Program
    {
        static void Main(string[] args)
        {
            TestLoops();
            Console.Read();
        }

        private static async void TestLoops()
        {
            for (int i = 0; i < 100; i++)
            {
                await TestAsync(i);
            }
        }

        private static Task TestAsync(int i)
        {
            return Task.Run(() => TaskToDo(i));
        }

        private async static void TaskToDo(int i)
        {
            await Task.Delay(10);
            Console.WriteLine(i);
        }
    }
}

22

Để học nhanh nhất ..

  • Hiểu luồng thực thi phương thức (có sơ đồ): 3 phút

  • Câu hỏi nội tâm (lợi ích học tập): 1 phút

  • Nhanh chóng thông qua cú pháp đường: 5 phút

  • Chia sẻ sự nhầm lẫn của một nhà phát triển: 5 phút

  • Vấn đề: Nhanh chóng thay đổi triển khai mã thông thường trong thế giới thực sang mã Async: 2 phút

  • Tiếp theo là đâu?

Hiểu luồng thực thi phương thức (có sơ đồ): 3 phút

Trong hình ảnh này, chỉ cần tập trung vào # 6 (không có gì hơn) nhập mô tả hình ảnh ở đây

Ở bước thứ 6: Việc thực thi dừng ở đây vì nó đã hết việc. Để tiếp tục, nó cần một kết quả từ getStringTask (loại hàm). Do đó, nó sử dụng một awaittoán tử để tạm dừng tiến trình của nó và trả lại quyền điều khiển (năng suất) cho người gọi (của phương thức này mà chúng tôi đang thực hiện). Cuộc gọi thực tế đến getStringTask đã được thực hiện trước đó ở # 2. Ở # 2, một lời hứa đã được thực hiện để trả về kết quả chuỗi. Nhưng khi nào nó sẽ trả về kết quả? Chúng ta có nên (# 1: AccessTheWebAsync) thực hiện lại cuộc gọi thứ 2 không? Ai nhận được kết quả, # 2 (tuyên bố gọi) hoặc # 6 (chờ tuyên bố)

Trình gọi bên ngoài của AccessTheWebAsync () cũng đang chờ. Vì vậy, người gọi đang chờ AccessTheWebAsync và AccessTheWebAsync đang chờ GetStringAsync vào lúc này. Điều thú vị là AccessTheWebAsync đã thực hiện một số công việc trước khi chờ đợi (# 4) có lẽ để tiết kiệm thời gian chờ đợi. Sự tự do tương tự đối với đa nhiệm cũng có sẵn cho người gọi bên ngoài (và tất cả những người gọi trong chuỗi) và đây là điểm cộng lớn nhất của điều 'không đồng bộ' này! Bạn cảm thấy như nó là đồng bộ..hoặc bình thường nhưng không phải vậy.

Hãy nhớ rằng, phương thức đã được trả về (# 2), nó không thể quay lại lần nữa (không có lần thứ hai). Vậy người gọi sẽ biết như thế nào? Đó là tất cả về Nhiệm vụ! Nhiệm vụ đã được thông qua. Nhiệm vụ đã được chờ đợi (không phải phương thức, không phải giá trị). Giá trị sẽ được đặt trong Nhiệm vụ. Trạng thái nhiệm vụ sẽ được thiết lập để hoàn thành. Người gọi chỉ cần theo dõi Nhiệm vụ (# 6). Vì vậy, 6 # là câu trả lời cho nơi / ai nhận được kết quả. Đọc thêm cho sau này ở đây .

Câu hỏi nội tâm cho việc học vì lợi ích: 1 phút

Hãy để chúng tôi điều chỉnh câu hỏi một chút:

Làm thế nào và khi nào sử dụng ? asyncawait Tasks

Bởi vì việc học Tasktự động bao gồm hai điều còn lại (và trả lời câu hỏi của bạn)

Nhanh chóng thông qua cú pháp đường: 5 phút

  • Trước khi chuyển đổi (phương thức ban đầu)

    internal static int Method(int arg0, int arg1) { int result = arg0 + arg1; IO(); // Do some long running IO. return result; }

  • một phương thức Nhiệm vụ để gọi phương thức trên

    internal static Task<int> MethodTask(int arg0, int arg1) { Task<int> task = new Task<int>(() => Method(arg0, arg1)); task.Start(); // Hot task (started task) should always be returned. return task; }

Chúng tôi đã đề cập đến chờ đợi hoặc không đồng bộ? Không. Gọi phương thức trên và bạn nhận được một nhiệm vụ mà bạn có thể theo dõi. Bạn đã biết những gì nhiệm vụ trả về .. một số nguyên.

  • Gọi một nhiệm vụ hơi khó khăn và đó là khi các từ khóa bắt đầu xuất hiện. Hãy để chúng tôi gọi Phương thức ()

    internal static async Task<int> MethodAsync(int arg0, int arg1) { int result = await HelperMethods.MethodTask(arg0, arg1); return result; }

Cùng một mã ở trên được thêm vào như hình ảnh dưới đây: nhập mô tả hình ảnh ở đây

  1. Chúng tôi đang 'chờ' nhiệm vụ được hoàn thành. Vì thếawait
  2. Vì chúng tôi sử dụng await, chúng tôi phải sử dụng async(cú pháp bắt buộc)
  3. Phương thức Đồng bộ hóa với Asynctư cách là tiền tố (tiêu chuẩn mã hóa)

awaitlà dễ hiểu nhưng hai ( async, Async) còn lại có thể không :). Chà, nó sẽ có ý nghĩa hơn nhiều đối với trình biên dịch mặc dù. Đọc thêm cho lần sau tại đây

Vậy là có 2 phần.

  1. Tạo 'Nhiệm vụ'
  2. Tạo đường cú pháp để gọi tác vụ ( await+async)

Hãy nhớ rằng, chúng tôi đã có một người gọi bên ngoài đến AccessTheWebAsync () và người gọi đó cũng không được tha thứ ... tức là nó cũng cần như await+asyncvậy. Và chuỗi tiếp tục. Nhưng sẽ luôn có một Taskkết thúc.

Tất cả đều ổn, nhưng một nhà phát triển đã ngạc nhiên khi thấy # 1 (Nhiệm vụ) bị thiếu ...

Chia sẻ sự nhầm lẫn của một nhà phát triển: 5 phút

Một nhà phát triển đã phạm sai lầm khi không thực hiện Tasknhưng nó vẫn hoạt động! Cố gắng hiểu câu hỏi và chỉ câu trả lời được chấp nhận được cung cấp ở đây . Hy vọng bạn đã đọc và hiểu đầy đủ. Tóm tắt là chúng ta có thể không thấy / triển khai 'Nhiệm vụ' nhưng nó được triển khai ở đâu đó trong lớp cha. Tương tự như vậy trong ví dụ của chúng ta, việc gọi một phương thức đã được xây dựng MethodAsync()dễ dàng hơn nhiều so với việc thực hiện phương thức đó với một Task( MethodTask()) chính chúng ta. Hầu hết các nhà phát triển cảm thấy khó khăn để có được đầu óc của họ Taskstrong khi chuyển đổi mã thành mã không đồng bộ.

Mẹo: Cố gắng tìm một triển khai Async hiện có (như MethodAsynchoặc ToListAsync) để thuê ngoài độ khó. Vì vậy, chúng ta chỉ cần xử lý Async và chờ đợi (dễ dàng và khá giống với mã thông thường)

Vấn đề: Nhanh chóng thay đổi triển khai mã thông thường trong thế giới thực sang hoạt động Async: 2 phút

Dòng mã hiển thị bên dưới trong Lớp dữ liệu bắt đầu bị phá vỡ (nhiều nơi). Bởi vì chúng tôi đã cập nhật một số mã của chúng tôi từ .Net framework 4.2. * Thành .Net core. Chúng tôi đã phải sửa lỗi này trong 1 giờ trên toàn bộ ứng dụng!

var myContract = query.Where(c => c.ContractID == _contractID).First();

dễ như ăn bánh!

  1. Chúng tôi đã cài đặt gói nuget EntityFramework vì nó có QueryableExtensions. Hay nói cách khác, nó thực hiện Async (tác vụ), vì vậy chúng ta có thể tồn tại với mã đơn giản Asyncawaitbằng mã.
  2. không gian tên = Microsoft.EntityFrameworkCore

dòng mã gọi đã thay đổi như thế này

var myContract = await query.Where(c => c.ContractID == _contractID).FirstAsync();
  1. Chữ ký phương thức thay đổi từ

    Contract GetContract(int contractnumber)

    đến

    async Task<Contract> GetContractAsync(int contractnumber)

  2. phương thức gọi cũng bị ảnh hưởng: GetContractAsync(123456);được gọi làGetContractAsync(123456).Result;

  3. Chúng tôi đã thay đổi nó ở khắp mọi nơi trong 30 phút!

Nhưng kiến ​​trúc sư bảo chúng tôi không sử dụng thư viện EntityFramework chỉ cho việc này! Giáo sư! kịch! Sau đó, chúng tôi đã thực hiện một nhiệm vụ tùy chỉnh (yuk). Mà bạn biết làm thế nào. Vẫn dễ! .. cho đến khi bạn

Tiếp theo là đâu? Có một video nhanh tuyệt vời mà chúng ta có thể xem về Chuyển đổi các cuộc gọi đồng bộ thành không đồng bộ trong Lõi ASP.Net , có lẽ đó là hướng đi mà người ta sẽ đi sau khi đọc điều này.


câu trả lời tuyệt vời! điều này đã giúp tôi rất nhiều
cklimowski

1
Câu trả lời tốt đẹp. Bạn chỉ có thể muốn sửa một vài điều nhỏ như: (a) đề cập đến ".Net framework 4.2" (không có phiên bản nào tôi biết, tồn tại) (b) trong EntityFrameWork =>
EntityFramework

15

Tất cả các câu trả lời ở đây sử dụng Task.Delay()hoặc một số asyncchức năng được xây dựng khác . Nhưng đây là ví dụ của tôi không sử dụng bất kỳ asyncchức năng nào trong số đó :

// Starts counting to a large number and then immediately displays message "I'm counting...". 
// Then it waits for task to finish and displays "finished, press any key".
static void asyncTest ()
{
    Console.WriteLine("Started asyncTest()");
    Task<long> task = asyncTest_count();
    Console.WriteLine("Started counting, please wait...");
    task.Wait(); // if you comment this line you will see that message "Finished counting" will be displayed before we actually finished counting.
    //Console.WriteLine("Finished counting to " + task.Result.ToString()); // using task.Result seems to also call task.Wait().
    Console.WriteLine("Finished counting.");
    Console.WriteLine("Press any key to exit program.");
    Console.ReadLine();
}

static async Task<long> asyncTest_count()
{
    long k = 0;
    Console.WriteLine("Started asyncTest_count()");
    await Task.Run(() =>
    {
        long countTo = 100000000;
        int prevPercentDone = -1;
        for (long i = 0; i <= countTo; i++)
        {
            int percentDone = (int)(100 * (i / (double)countTo));
            if (percentDone != prevPercentDone)
            {
                prevPercentDone = percentDone;
                Console.Write(percentDone.ToString() + "% ");
            }

            k = i;
        }
    });
    Console.WriteLine("");
    Console.WriteLine("Finished asyncTest_count()");
    return k;
}

2
Cảm ơn bạn! câu trả lời đầu tiên thực sự làm một số công việc thay vì chờ đợi.
Jeffnl

cảm ơn bạn đã hiển thị task.Wait();và làm thế nào nó có thể được sử dụng để tránh async / await hell: P
encoder

12

Câu trả lời này nhằm cung cấp một số thông tin cụ thể cho ASP.NET.

Bằng cách sử dụng async / await trong bộ điều khiển MVC, có thể tăng mức sử dụng nhóm luồng và đạt được thông lượng tốt hơn nhiều, như được giải thích trong bài viết dưới đây,

http://www.asp.net/mvc/tutorials/mvc-4/USE-asynyncous-methods-in-aspnet-mvc-4

Trong các ứng dụng web nhìn thấy một số lượng lớn các yêu cầu đồng thời khi khởi động hoặc có tải lớn (khi đồng thời tăng đột ngột), làm cho các cuộc gọi dịch vụ web này không đồng bộ sẽ tăng khả năng phản hồi của ứng dụng của bạn. Một yêu cầu không đồng bộ mất cùng thời gian để xử lý như một yêu cầu đồng bộ. Ví dụ: nếu một yêu cầu thực hiện cuộc gọi dịch vụ web cần hai giây để hoàn thành, yêu cầu sẽ mất hai giây cho dù nó được thực hiện đồng bộ hay không đồng bộ. Tuy nhiên, trong một cuộc gọi không đồng bộ, một luồng không bị chặn đáp ứng các yêu cầu khác trong khi nó chờ yêu cầu đầu tiên hoàn thành. Do đó, các yêu cầu không đồng bộ ngăn chặn yêu cầu hàng đợi và tăng trưởng nhóm luồng khi có nhiều yêu cầu đồng thời gọi các hoạt động chạy dài.


12

Async & đang chờ giải thích đơn giản

Tương tự đơn giản

Một người có thể đợi chuyến tàu buổi sáng của họ. Đây là tất cả những gì họ đang làm vì đây là nhiệm vụ chính của họ mà họ hiện đang thực hiện. (lập trình đồng bộ (những gì bạn thường làm!))

Một người khác có thể đợi chuyến tàu buổi sáng của họ trong khi họ hút thuốc và sau đó uống cà phê. (Lập trình không đồng bộ)

Lập trình không đồng bộ là gì?

Lập trình không đồng bộ là nơi lập trình viên sẽ chọn chạy một số mã của mình trên một luồng riêng biệt từ luồng thực thi chính và sau đó thông báo cho luồng chính khi hoàn thành.

Từ khóa async thực sự làm gì?

Tiền tố từ khóa async cho một tên phương thức như

async void DoSomething(){ . . .

cho phép lập trình viên sử dụng từ khóa await khi gọi các tác vụ không đồng bộ. Đó là tất cả những gì nó làm.

Sao nó lại quan trọng?

Trong rất nhiều hệ thống phần mềm, luồng chính được dành riêng cho các hoạt động liên quan cụ thể đến Giao diện người dùng. Nếu tôi đang chạy một thuật toán đệ quy rất phức tạp, mất 5 giây để hoàn thành trên máy tính của tôi, nhưng tôi đang chạy nó trên Chủ đề chính (luồng UI) Khi người dùng cố gắng nhấp vào bất cứ thứ gì trên ứng dụng của tôi, nó sẽ bị đóng băng vì chủ đề chính của tôi đã xếp hàng và hiện đang xử lý quá nhiều hoạt động. Kết quả là luồng chính không thể xử lý nhấp chuột để chạy phương thức từ nút bấm.

Khi nào bạn sử dụng Async và Await?

Sử dụng các từ khóa không đồng bộ lý tưởng khi bạn đang làm bất cứ điều gì không liên quan đến giao diện người dùng.

Vì vậy, giả sử bạn đang viết một chương trình cho phép người dùng phác thảo trên điện thoại di động của họ, nhưng cứ sau 5 giây, nó sẽ kiểm tra thời tiết trên internet.

Chúng ta nên chờ cuộc gọi bỏ phiếu cứ sau 5 giây vào mạng để biết thời tiết vì người dùng ứng dụng cần tiếp tục tương tác với màn hình cảm ứng di động để vẽ những bức ảnh đẹp.

Làm thế nào để bạn sử dụng Async và Await

Sau đây từ ví dụ trên, đây là một số mã giả về cách viết nó:

    //ASYNCHRONOUS
    //this is called using the await keyword every 5 seconds from a polling timer or something.

    async Task CheckWeather()
    {
        var weather = await GetWeather();
        //do something with the weather now you have it
    }

    async Task<WeatherResult> GetWeather()
    {

        var weatherJson = await CallToNetworkAddressToGetWeather();
        return deserializeJson<weatherJson>(weatherJson);
    }

    //SYNCHRONOUS
    //This method is called whenever the screen is pressed
    void ScreenPressed()
    {
        DrawSketchOnScreen();
    }

Ghi chú bổ sung - Cập nhật

Tôi đã quên đề cập đến trong ghi chú ban đầu của mình rằng trong C #, bạn chỉ có thể chờ đợi các phương thức được gói trong Nhiệm vụ. ví dụ bạn có thể chờ đợi phương pháp này:

// awaiting this will return a string.
// calling this without await (synchronously) will result in a Task<string> object.
async Task<string> FetchHelloWorld() {..

Bạn không thể chờ đợi các phương thức không phải là nhiệm vụ như thế này:

async string FetchHelloWorld() {..

Vui lòng xem lại mã nguồn cho lớp Nhiệm vụ tại đây .


4
Cảm ơn đã dành thời gian để viết cái này.
Prashant

10

Không đồng bộ / Đang chờ

Trên thực tế Async / Await là một cặp từ khóa chỉ là đường cú pháp để tạo ra một cuộc gọi lại của một nhiệm vụ không đồng bộ.

Lấy ví dụ thao tác này:

    public static void DoSomeWork()
    {
        var task = Task.Run(() =>
        {
            // [RUNS ON WORKER THREAD]

            // IS NOT bubbling up due to the different threads
            throw new Exception();
            Thread.Sleep(2000);

            return "Hello";
        });

        // This is the callback
        task.ContinueWith((t) => {
            // -> Exception is swallowed silently
            Console.WriteLine("Completed");

            // [RUNS ON WORKER THREAD]
        });
    }

Các mã trên có một số nhược điểm. Lỗi không được truyền lại và thật khó đọc. Nhưng Async và Await đến để giúp chúng tôi:

    public async static void DoSomeWork()
    {
        var result = await Task.Run(() =>
        {
            // [RUNS ON WORKER THREAD]

            // IS bubbling up
            throw new Exception();
            Thread.Sleep(2000);

            return "Hello";
        });

        // every thing below is a callback 
        // (including the calling methods)

        Console.WriteLine("Completed");

    }

Chờ cuộc gọi phải ở trong phương thức Async. Điều này có một số lợi thế:

  • Trả về kết quả của Nhiệm vụ
  • tự động tạo ra một cuộc gọi lại
  • kiểm tra lỗi và cho phép chúng nổi bong bóng trong callstack (chỉ tối đa các cuộc gọi không chờ đợi trong callstack)
  • chờ kết quả
  • giải phóng chủ đề chính
  • chạy gọi lại trên luồng chính
  • sử dụng một luồng công nhân từ luồng này cho nhiệm vụ
  • làm cho mã dễ đọc
  • và nhiều hơn nữa

LƯU Ý : Async và Await được sử dụng với các cuộc gọi không đồng bộ không thực hiện các cuộc gọi này. Bạn phải sử dụng Task Libary cho việc này, như Task.Run ().

Dưới đây là so sánh giữa chờ đợi và không chờ đợi giải pháp

Đây không phải là giải pháp không đồng bộ:

    public static long DoTask()
    {
        stopWatch.Reset();
        stopWatch.Start();

        // [RUNS ON MAIN THREAD]
        var task = Task.Run(() => {
            Thread.Sleep(2000);
            // [RUNS ON WORKER THREAD]
        });
        // goes directly further
        // WITHOUT waiting until the task is finished

        // [RUNS ON MAIN THREAD]

        stopWatch.Stop();
        // 50 milliseconds
        return stopWatch.ElapsedMilliseconds;
    }

Đây là phương pháp không đồng bộ:

    public async static Task<long> DoAwaitTask()
    {
        stopWatch.Reset();
        stopWatch.Start();

        // [RUNS ON MAIN THREAD]

        await Task.Run(() => {
            Thread.Sleep(2000);
            // [RUNS ON WORKER THREAD]
        });
        // Waits until task is finished

        // [RUNS ON MAIN THREAD]

        stopWatch.Stop();
        // 2050 milliseconds
        return stopWatch.ElapsedMilliseconds;
    }

Bạn thực sự có thể gọi một phương thức async mà không cần từ khóa chờ đợi nhưng điều này có nghĩa là mọi Ngoại lệ ở đây đều bị nuốt trong chế độ phát hành:

    public static Stopwatch stopWatch { get; } = new Stopwatch();

    static void Main(string[] args)
    {
        Console.WriteLine("DoAwaitTask: " + DoAwaitTask().Result + " ms");
        // 2050 (2000 more because of the await)
        Console.WriteLine("DoTask: " + DoTask() + " ms");
        // 50
        Console.ReadKey();
    }

Async và Await không có nghĩa là tính toán song song. Chúng được sử dụng để không chặn chủ đề chính của bạn. Khi nói về ứng dụng asp.net hoặc Windows, việc chặn luồng chính của bạn do cuộc gọi mạng là một điều tồi tệ. Nếu bạn làm điều này, ứng dụng của bạn sẽ không phản hồi hoặc thậm chí sụp đổ.

Kiểm tra tài liệu ms để biết thêm ví dụ.


9

Thành thật mà nói tôi vẫn nghĩ rằng lời giải thích tốt nhất là lời giải thích về tương lai và những lời hứa trên Wikipedia: http://en.wikipedia.org/wiki/Futures_and_promises

Ý tưởng cơ bản là bạn có một nhóm các luồng riêng biệt thực thi các tác vụ không đồng bộ. Khi sử dụng nó. Tuy nhiên, đối tượng thực hiện lời hứa rằng nó sẽ thực hiện thao tác tại một số thời điểm và cung cấp cho bạn kết quả khi bạn yêu cầu. Điều này có nghĩa là nó sẽ chặn khi bạn yêu cầu kết quả và chưa kết thúc, nhưng thực thi trong nhóm luồng khác.

Từ đó bạn có thể tối ưu hóa mọi thứ: một số thao tác có thể được triển khai không đồng bộ và bạn có thể tối ưu hóa những thứ như tệp IO và giao tiếp mạng bằng cách gộp các yêu cầu tiếp theo và / hoặc sắp xếp lại chúng. Tôi không chắc liệu điều này đã có trong khung nhiệm vụ của Microsoft chưa - nhưng nếu đó không phải là một trong những điều đầu tiên tôi sẽ thêm vào.

Bạn thực sự có thể triển khai sắp xếp mẫu tương lai với sản lượng trong C # 4.0. Nếu bạn muốn biết chính xác cách thức hoạt động của nó, tôi có thể đề xuất liên kết này thực hiện công việc tốt: http://code.google.com.vn/p/fracture/source/browse/trunk/Squared/TaskLib/ . Tuy nhiên, nếu bạn bắt đầu tự chơi với nó, bạn sẽ nhận thấy rằng bạn thực sự cần hỗ trợ ngôn ngữ nếu bạn muốn làm tất cả những điều tuyệt vời - đó chính xác là những gì Microsoft đã làm.


8

Xem fiddle https://dotnetfiddle.net/VhZdLU này (và cải thiện nó nếu có thể) để chạy một ứng dụng bảng điều khiển đơn giản hiển thị các cách sử dụng của Task, Task.WaitAll (), async và chờ đợi các nhà khai thác trong cùng một chương trình.

Fiddle này sẽ xóa khái niệm chu kỳ thực hiện của bạn.

Đây là mã mẫu

using System;
using System.Threading.Tasks;

public class Program
{
    public static void Main()
    {               
        var a = MyMethodAsync(); //Task started for Execution and immediately goes to Line 19 of the code. Cursor will come back as soon as await operator is met       
        Console.WriteLine("Cursor Moved to Next Line Without Waiting for MyMethodAsync() completion");
        Console.WriteLine("Now Waiting for Task to be Finished");       
        Task.WaitAll(a); //Now Waiting      
        Console.WriteLine("Exiting CommandLine");       
    }

    public static async Task MyMethodAsync()
    {
        Task<int> longRunningTask = LongRunningOperation();
        // independent work which doesn't need the result of LongRunningOperationAsync can be done here
        Console.WriteLine("Independent Works of now executes in MyMethodAsync()");
        //and now we call await on the task 
        int result = await longRunningTask;
        //use the result 
        Console.WriteLine("Result of LongRunningOperation() is " + result);
    }

    public static async Task<int> LongRunningOperation() // assume we return an int from this long running operation 
    {
        Console.WriteLine("LongRunningOperation() Started");
        await Task.Delay(2000); // 2 second delay
        Console.WriteLine("LongRunningOperation() Finished after 2 Seconds");
        return 1;
    }   

}

Dấu vết đến từ Cửa sổ đầu ra: nhập mô tả hình ảnh ở đây


3
public static void Main(string[] args)
{
    string result = DownloadContentAsync().Result;
    Console.ReadKey();
}

// You use the async keyword to mark a method for asynchronous operations.
// The "async" modifier simply starts synchronously the current thread. 
// What it does is enable the method to be split into multiple pieces.
// The boundaries of these pieces are marked with the await keyword.
public static async Task<string> DownloadContentAsync()// By convention, the method name ends with "Async
{
    using (HttpClient client = new HttpClient())
    {
        // When you use the await keyword, the compiler generates the code that checks if the asynchronous operation is finished.
        // If it is already finished, the method continues to run synchronously.
        // If not completed, the state machine will connect a continuation method that must be executed WHEN the Task is completed.


        // Http request example. 
        // (In this example I can set the milliseconds after "sleep=")
        String result = await client.GetStringAsync("http://httpstat.us/200?sleep=1000");

        Console.WriteLine(result);

        // After completing the result response, the state machine will continue to synchronously execute the other processes.


        return result;
    }
}

3

Ở cấp độ cao hơn:

1) Từ khóa Async cho phép chờ đợi và đó là tất cả những gì nó làm. Từ khóa Async không chạy phương thức trong một luồng riêng biệt. Phương thức f async bắt đầu chạy đồng bộ cho đến khi nó chờ đợi trong một nhiệm vụ tốn thời gian.

2) Bạn có thể chờ đợi một phương thức trả về Nhiệm vụ hoặc Nhiệm vụ loại T. Bạn không thể chờ đợi trên phương thức void async.

3) Thời điểm gặp gỡ chủ đề chính đang chờ đợi trong nhiệm vụ tốn thời gian hoặc khi công việc thực tế được bắt đầu, luồng chính sẽ trả về cho người gọi phương thức hiện tại.

4) Nếu luồng chính thấy chờ một tác vụ vẫn đang thực thi, thì nó không chờ nó và trả về cho người gọi phương thức hiện tại. Theo cách này, ứng dụng vẫn đáp ứng.

5) Đang chờ xử lý tác vụ, bây giờ sẽ thực thi trên một luồng riêng biệt từ nhóm luồng.

6) Khi nhiệm vụ chờ đợi này được hoàn thành, tất cả các mã bên dưới nó sẽ được thực hiện bởi các luồng riêng biệt

Dưới đây là mã mẫu. Thực thi nó và kiểm tra id luồng

using System;
using System.Threading;
using System.Threading.Tasks;

namespace AsyncAwaitDemo
{
    class Program
    {
        public static async void AsynchronousOperation()
        {
            Console.WriteLine("Inside AsynchronousOperation Before AsyncMethod, Thread Id: " + Thread.CurrentThread.ManagedThreadId);
            //Task<int> _task = AsyncMethod();
            int count = await AsyncMethod();

            Console.WriteLine("Inside AsynchronousOperation After AsyncMethod Before Await, Thread Id: " + Thread.CurrentThread.ManagedThreadId);

            //int count = await _task;

            Console.WriteLine("Inside AsynchronousOperation After AsyncMethod After Await Before DependentMethod, Thread Id: " + Thread.CurrentThread.ManagedThreadId);

            DependentMethod(count);

            Console.WriteLine("Inside AsynchronousOperation After AsyncMethod After Await After DependentMethod, Thread Id: " + Thread.CurrentThread.ManagedThreadId);
        }

        public static async Task<int> AsyncMethod()
        {
            Console.WriteLine("Inside AsyncMethod, Thread Id: " + Thread.CurrentThread.ManagedThreadId);
            int count = 0;

            await Task.Run(() =>
            {
                Console.WriteLine("Executing a long running task which takes 10 seconds to complete, Thread Id: " + Thread.CurrentThread.ManagedThreadId);
                Thread.Sleep(20000);
                count = 10;
            });

            Console.WriteLine("Completed AsyncMethod, Thread Id: " + Thread.CurrentThread.ManagedThreadId);

            return count;
        }       

        public static void DependentMethod(int count)
        {
            Console.WriteLine("Inside DependentMethod, Thread Id: " + Thread.CurrentThread.ManagedThreadId + ". Total count is " + count);
        }

        static void Main(string[] args)
        {
            Console.WriteLine("Started Main method, Thread Id: " + Thread.CurrentThread.ManagedThreadId);

            AsynchronousOperation();

            Console.WriteLine("Completed Main method, Thread Id: " + Thread.CurrentThread.ManagedThreadId);

            Console.ReadKey();
        }

    }
}

2

Theo cách hiểu của tôi, nó cũng nên có một thuật ngữ thứ ba được thêm vào hỗn hợp : Task.

Async chỉ là một vòng loại bạn đưa vào phương thức của mình để nói rằng đó là một phương thức không đồng bộ.

Tasklà sự trở lại của asynchàm. Nó thực thi không đồng bộ.

Bạn awaitlà một nhiệm vụ. Khi thực thi mã đạt đến dòng này, điều khiển nhảy trở lại để gọi hàm của chức năng ban đầu xung quanh bạn.

Nếu thay vào đó, bạn gán sự trở lại của một asyncchức năng (ví dụ Task) cho một biến, khi mã thực hiện đạt dòng này, nó chỉ tiếp tục qua dòng đó trong hàm xung quanh trong khi các Taskthực thi đồng bộ.


1

đang sử dụng chúng bằng với các chủ đề nền sinh sản để thực hiện logic thời gian dài?

Bài viết này MDSN: Lập trình không đồng bộ với async và await (C #) giải thích rõ ràng:

Các từ khóa không đồng bộ và chờ đợi không gây ra các chủ đề bổ sung được tạo. Các phương thức async không yêu cầu đa luồng vì một phương thức async không chạy trên luồng của chính nó. Phương thức chạy trên bối cảnh đồng bộ hóa hiện tại và chỉ sử dụng thời gian trên luồng khi phương thức được kích hoạt.


1

Trong đoạn mã sau, phương thức HttpClient GetByteArrayAsync trả về một Tác vụ, getContentsTask. Tác vụ là một lời hứa để tạo ra mảng byte thực tế khi tác vụ hoàn thành. Toán tử await được áp dụng cho getContentsTask để tạm dừng thực thi trong SumPageSizesAsync cho đến khi getContentsTask hoàn tất. Trong thời gian chờ đợi, quyền điều khiển được trả về cho người gọi SumPageSizesAsync. Khi getContentsTask kết thúc, biểu thức await ước tính thành một mảng byte.

private async Task SumPageSizesAsync()
{
    // To use the HttpClient type in desktop apps, you must include a using directive and add a 
    // reference for the System.Net.Http namespace.
    HttpClient client = new HttpClient();
    // . . .
    Task<byte[]> getContentsTask = client.GetByteArrayAsync(url);
    byte[] urlContents = await getContentsTask;

    // Equivalently, now that you see how it works, you can write the same thing in a single line.
    //byte[] urlContents = await client.GetByteArrayAsync(url);
    // . . .
}

1

Dưới đây là mã đọc tệp excel bằng cách mở hộp thoại và sau đó sử dụng async và chờ để chạy không đồng bộ mã đọc từng dòng một từ excel và liên kết với lưới

namespace EmailBillingRates
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            lblProcessing.Text = "";
        }

        private async void btnReadExcel_Click(object sender, EventArgs e)
        {
            string filename = OpenFileDialog();

            Microsoft.Office.Interop.Excel.Application xlApp = new Microsoft.Office.Interop.Excel.Application();
            Microsoft.Office.Interop.Excel.Workbook xlWorkbook = xlApp.Workbooks.Open(filename);
            Microsoft.Office.Interop.Excel._Worksheet xlWorksheet = xlWorkbook.Sheets[1];
            Microsoft.Office.Interop.Excel.Range xlRange = xlWorksheet.UsedRange;
            try
            {
                Task<int> longRunningTask = BindGrid(xlRange);
                int result = await longRunningTask;

            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message.ToString());
            }
            finally
            {
                //cleanup  
               // GC.Collect();
                //GC.WaitForPendingFinalizers();

                //rule of thumb for releasing com objects:  
                //  never use two dots, all COM objects must be referenced and released individually  
                //  ex: [somthing].[something].[something] is bad  

                //release com objects to fully kill excel process from running in the background  
                Marshal.ReleaseComObject(xlRange);
                Marshal.ReleaseComObject(xlWorksheet);

                //close and release  
                xlWorkbook.Close();
                Marshal.ReleaseComObject(xlWorkbook);

                //quit and release  
                xlApp.Quit();
                Marshal.ReleaseComObject(xlApp);
            }

        }

        private void btnSendEmail_Click(object sender, EventArgs e)
        {

        }

        private string OpenFileDialog()
        {
            string filename = "";
            OpenFileDialog fdlg = new OpenFileDialog();
            fdlg.Title = "Excel File Dialog";
            fdlg.InitialDirectory = @"c:\";
            fdlg.Filter = "All files (*.*)|*.*|All files (*.*)|*.*";
            fdlg.FilterIndex = 2;
            fdlg.RestoreDirectory = true;
            if (fdlg.ShowDialog() == DialogResult.OK)
            {
                filename = fdlg.FileName;
            }
            return filename;
        }

        private async Task<int> BindGrid(Microsoft.Office.Interop.Excel.Range xlRange)
        {
            lblProcessing.Text = "Processing File.. Please wait";
            int rowCount = xlRange.Rows.Count;
            int colCount = xlRange.Columns.Count;

            // dt.Column = colCount;  
            dataGridView1.ColumnCount = colCount;
            dataGridView1.RowCount = rowCount;

            for (int i = 1; i <= rowCount; i++)
            {
                for (int j = 1; j <= colCount; j++)
                {
                    //write the value to the Grid  
                    if (xlRange.Cells[i, j] != null && xlRange.Cells[i, j].Value2 != null)
                    {
                         await Task.Delay(1);
                         dataGridView1.Rows[i - 1].Cells[j - 1].Value =  xlRange.Cells[i, j].Value2.ToString();
                    }

                }
            }
            lblProcessing.Text = "";
            return 0;
        }
    }

    internal class async
    {
    }
}

0

Các câu trả lời ở đây rất hữu ích như một hướng dẫn chung về await / async. Chúng cũng chứa một số chi tiết về cách await / async được nối dây. Tôi muốn chia sẻ một số kinh nghiệm thực tế với bạn mà bạn nên biết trước khi sử dụng mẫu thiết kế này.

Thuật ngữ "chờ đợi" là theo nghĩa đen, vì vậy bất kỳ chủ đề nào bạn gọi nó sẽ chờ kết quả của phương pháp trước khi tiếp tục. Trên chủ đề nền trước , đây là một thảm họa . Chủ đề nền trước mang gánh nặng xây dựng ứng dụng của bạn, bao gồm các chế độ xem, mô hình xem, hoạt ảnh ban đầu và bất cứ điều gì khác mà bạn đã khởi động với các yếu tố đó. Vì vậy, khi bạn chờ đợi chủ đề tiền cảnh, bạn dừng lại ứng dụng. Người dùng chờ đợi và chờ đợi khi không có gì xảy ra. Điều này cung cấp trải nghiệm người dùng tiêu cực.

Bạn chắc chắn có thể chờ đợi một chủ đề nền bằng nhiều phương tiện:

Device.BeginInvokeOnMainThread(async () => { await AnyAwaitableMethod(); });

// Notice that we do not await the following call, 
// as that would tie it to the foreground thread.
try
{
Task.Run(async () => { await AnyAwaitableMethod(); });
}
catch
{}

Mã hoàn chỉnh cho những nhận xét này có tại https://github.com/marcowers/xamarin-forms-annoyances . Xem giải pháp được gọi là AwaitAsyncAntipotype.sln.

Trang web GitHub cũng cung cấp các liên kết đến một cuộc thảo luận chi tiết hơn về chủ đề này.


1
Từ những gì tôi hiểu, async / awaitlà đường cú pháp cho các cuộc gọi lại, nó không có gì để làm với luồng. msdn.microsoft.com/en-us/magazine/hh456401.aspx Nó dành cho mã không bị ràng buộc của CPU, ví dụ như chờ nhập liệu hoặc trì hoãn. Task.Runchỉ nên được sử dụng cho blog
geometrikal

The term "await" is literal, so whatever thread you call it on will wait for the result of the method before continuing.Điều này không đúng - có lẽ ý bạn là Task.Wait ()? Khi bạn sử dụng await, nó đặt phần còn lại của phương thức thành phần tiếp theo được thực thi khi bất cứ điều gì bạn chờ đợi-ed hoàn tất. Nó thoát khỏi phương thức mà bạn đã sử dụng, vì vậy người gọi có thể tiếp tục. Sau đó, khi dòng await-ed thực sự hoàn thành, nó sẽ hoàn thành phần còn lại của phương thức đó trên một số luồng (thường là một luồng công nhân).
Don Cheadle

@geometrikal ở cốt lõi, async/awaitlà về giải phóng .NET Themes . Khi bạn awaitthực hiện thao tác không đồng bộ (như File của .NET.WriteAsync), nó sẽ tạm dừng phần còn lại của phương thức bạn đã sử dụng awaitđể người gọi có thể tiếp tục và có khả năng hoàn thành mục đích của mình. Không có chặn luồng hoặc chờ cho awaithoạt động -ed. Khi thao tác bạn awaited hoàn thành, phần còn lại của async/awaitphương thức được đặt trên một luồng và được thực thi (tương tự như ý tưởng gọi lại).
Don Cheadle
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.