Đợi một phương thức void async


155

Làm thế nào tôi có thể chờ đợi một void asyncphương pháp để hoàn thành công việc của nó?

ví dụ, tôi có một chức năng như dưới đây:

async void LoadBlahBlah()
{
    await blah();
    ...
}

bây giờ tôi muốn chắc chắn rằng mọi thứ đã được tải trước khi tiếp tục ở một nơi khác.

Câu trả lời:


234

Thực hành tốt nhất là chỉ đánh dấu chức năng async voidnếu nó là phương thức cháy và quên, nếu bạn muốn chờ đợi, bạn nên đánh dấu nó là async Task.

Trong trường hợp nếu bạn vẫn muốn chờ đợi, thì hãy bọc nó như vậy await Task.Run(() => blah())


Blah rõ ràng là trả lại nhiệm vụ và chữ ký ha async tại sao bạn sẽ gọi nó mà không chờ đợi. thậm chí VS sẽ cho bạn cảnh báo về nó.
batmaci

8
await Task.Run(() => An_async_void_method_I_can_not_modify_now())
lợi ích

1
await Task.Run(() => blah())là sai lệch. Điều này không chờ đợi hoàn thành chức năng async blah, nó chỉ chờ việc tạo (tầm thường) của tác vụ và tiếp tục ngay lập tức trước khi blah()hoàn thành.
Jonathan Lidbeck

@JonathanLidbeck tuyên bố "tiếp tục ngay trước blah là sai Hãy thử. 'Chờ đợi Task.Run (() => Thread.Sleep (10_000))', nhiệm vụ được chờ đợi trong 10 giây + trước khi thực hiện bất kỳ dòng tiếp theo
Rohit Sharma

@RohitSharma, điều đó đúng với ví dụ của bạn, nhưng Thread.Sleepkhông đồng bộ. Câu hỏi này là về việc chờ đợi một async voidchức năng, nóiasync void blah() { Task.Delay(10000); }
Jonathan Lidbeck

59

Nếu bạn có thể thay đổi chữ ký của hàm thành async Taskthì bạn có thể sử dụng mã được trình bày ở đây


27

Giải pháp tốt nhất là sử dụng async Task. Bạn nên tránh async voidvì một số lý do, một trong số đó là khả năng kết hợp.

Nếu phương thức không thể được thực hiện để trả về Task(ví dụ: đó là một trình xử lý sự kiện), thì bạn có thể sử dụng SemaphoreSlimđể có tín hiệu phương thức khi nó sắp thoát. Xem xét làm điều này trong một finallykhối.


1

thực hiện AutoResetEvent, gọi hàm sau đó đợi AutoResetEvent và sau đó đặt nó vào khoảng trống async khi bạn biết nó đã hoàn thành.

Bạn cũng có thể đợi một Tác vụ trả về từ async void của bạn


1

Bạn không thực sự cần phải làm bất cứ điều gì bằng tay, awaittừ khóa tạm dừng thực thi chức năng cho đến khi blah()trả về.

private async void SomeFunction()
{
     var x = await LoadBlahBlah(); <- Function is not paused
     //rest of the code get's executed even if LoadBlahBlah() is still executing
}

private async Task<T> LoadBlahBlah()
{
     await DoStuff();  <- function is paused
     await DoMoreStuff();
}

Tlà loại đối tượng blah()trả về

Bạn thực sự không thể awaitlà một voidchức năng nên LoadBlahBlah()không thểvoid


Tôi muốn đợi LoadBlahBlah()kết thúc, không phảiblah()
MBZ

10
Thật không may, các phương thức void async không tạm dừng thực thi (không giống như các phương thức Nhiệm vụ async)
ghord

-1

Tôi biết đây là một câu hỏi cũ, nhưng đây vẫn là một vấn đề tôi tiếp tục gặp phải, nhưng vẫn chưa có giải pháp rõ ràng nào để thực hiện chính xác khi sử dụng async / await trong phương thức chữ ký async void.

Tuy nhiên, tôi nhận thấy rằng .Wait () đang hoạt động đúng trong phương thức void.

và vì async void và void có cùng chữ ký, bạn có thể cần phải làm như sau.

void LoadBlahBlah()
{
    blah().Wait(); //this blocks
}

Một cách khó hiểu là async / await không chặn mã tiếp theo.

async void LoadBlahBlah()
{
    await blah(); //this does not block
}

Khi bạn dịch ngược mã của mình, tôi đoán là async void tạo ra một Tác vụ bên trong (giống như Tác vụ không đồng bộ), nhưng vì chữ ký không hỗ trợ để trả về Nhiệm vụ bên trong đó

điều này có nghĩa là bên trong phương thức void async vẫn có thể "chờ" các phương thức async bên trong. nhưng bên ngoài không thể biết khi nào Nhiệm vụ nội bộ hoàn thành.

Vì vậy, kết luận của tôi là khoảng trống async hoạt động như dự định và nếu bạn cần phản hồi từ Tác vụ nội bộ, thì bạn cần sử dụng chữ ký Tác vụ async thay thế.

hy vọng lan man của tôi có ý nghĩa với bất cứ ai cũng đang tìm kiếm câu trả lời.

Chỉnh sửa: Tôi đã tạo một số mã ví dụ và dịch ngược nó để xem những gì đang thực sự xảy ra.

static async void Test()
{
    await Task.Delay(5000);
}

static async Task TestAsync()
{
    await Task.Delay(5000);
}

Biến thành (chỉnh sửa: Tôi biết rằng mã cơ thể không có ở đây mà là ở các stHRachines, nhưng về cơ bản thì các stHRachines giống hệt nhau, vì vậy tôi không bận tâm thêm chúng)

private static void Test()
{
    <Test>d__1 stateMachine = new <Test>d__1();
    stateMachine.<>t__builder = AsyncVoidMethodBuilder.Create();
    stateMachine.<>1__state = -1;
    AsyncVoidMethodBuilder <>t__builder = stateMachine.<>t__builder;
    <>t__builder.Start(ref stateMachine);
}
private static Task TestAsync()
{
    <TestAsync>d__2 stateMachine = new <TestAsync>d__2();
    stateMachine.<>t__builder = AsyncTaskMethodBuilder.Create();
    stateMachine.<>1__state = -1;
    AsyncTaskMethodBuilder <>t__builder = stateMachine.<>t__builder;
    <>t__builder.Start(ref stateMachine);
    return stateMachine.<>t__builder.Task;
}

cả AsyncVoidMethodBuilder hoặc AsyncTaskMethodBuilder thực sự không có bất kỳ mã nào trong phương thức Start sẽ gợi ý chúng để chặn và sẽ luôn chạy không đồng bộ sau khi chúng được khởi động.

nghĩa là không có Nhiệm vụ trở lại, sẽ không có cách nào để kiểm tra nếu nó hoàn thành.

như mong đợi, nó chỉ khởi động Tác vụ chạy không đồng bộ, và sau đó nó tiếp tục trong mã. và tác vụ không đồng bộ, đầu tiên, nó khởi động tác vụ và sau đó nó trả về nó.

Vì vậy, tôi đoán câu trả lời của tôi sẽ là không bao giờ sử dụng async void, nếu bạn cần biết khi nào nhiệm vụ được thực hiện, đó là nhiệm vụ async dành cho.

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.