Tunggu metode batal async

155

Bagaimana saya bisa menunggu void asyncmetode untuk menyelesaikan tugasnya?

misalnya, saya memiliki fungsi seperti di bawah ini:

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

sekarang saya ingin memastikan bahwa semuanya telah dimuat sebelum melanjutkan ke tempat lain.

MBZ
sumber

Jawaban:

234

Praktik terbaik adalah menandai fungsi async voidhanya jika itu api dan lupakan metode, jika Anda ingin menunggu, Anda harus menandainya sebagai async Task.

Jika Anda masih ingin menunggu, maka bungkuslah seperti itu await Task.Run(() => blah())

Rohit Sharma
sumber
bla jelas kembali tugas dan tanda tangan async mengapa Anda menyebutnya tanpa menunggu. bahkan VS akan memberi Anda peringatan tentang hal itu.
batmaci
8
await Task.Run(() => An_async_void_method_I_can_not_modify_now())
themefield
1
await Task.Run(() => blah())menyesatkan. Ini tidak menunggu selesainya fungsi async blah, itu hanya menunggu penciptaan (sepele) tugas, dan berlanjut segera sebelum blah()selesai.
Jonathan Lidbeck
@JonathanLidbeck pernyataan "terus segera sebelum bla salah. Coba" menunggu Task.Run (() => Thread.Sleep (10_000)) ", tugas tersebut menunggu selama 10 detik + sebelum menjalankan baris berikutnya
Rohit Sharma
@RohitSharma, itu benar untuk contoh Anda, tetapi Thread.Sleeptidak async. Pertanyaan ini adalah tentang menunggu suatu async voidfungsi, katakanlahasync void blah() { Task.Delay(10000); }
Jonathan Lidbeck
59

Jika Anda dapat mengubah tanda tangan fungsi async TaskAnda, maka Anda dapat menggunakan kode yang disajikan di sini

Komunitas
sumber
27

Solusi terbaik adalah menggunakan async Task. Anda harus menghindari async voidkarena beberapa alasan, salah satunya adalah kompabilitas.

Jika metode tidak dapat dibuat untuk kembali Task(misalnya, itu adalah pengendali acara), maka Anda dapat menggunakan SemaphoreSlimuntuk memiliki sinyal metode ketika akan keluar. Pertimbangkan untuk melakukan ini di finallyblok.

Stephen Cleary
sumber
1

lakukan AutoResetEvent, panggil fungsi lalu tunggu AutoResetEvent dan kemudian atur di dalam async void ketika Anda tahu itu selesai.

Anda juga bisa menunggu pada Tugas yang kembali dari kekosongan async Anda

pengguna2712345
sumber
1

Anda tidak perlu melakukan apa pun secara manual, awaitkata kunci menjeda eksekusi fungsi sampai blah()kembali.

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();
}

Tadalah jenis blah()pengembalian objek

Anda tidak bisa benar await- benar voidfungsi jadi LoadBlahBlah()tidak bisavoid

Mayank
sumber
Saya ingin menunggu LoadBlahBlah()sampai selesai, bukanblah()
MBZ
10
Sayangnya metode void async tidak menghentikan eksekusi (tidak seperti metode Task async)
ghord
-1

Saya tahu ini adalah pertanyaan lama, tetapi ini masih merupakan masalah yang terus saya hadapi, namun masih belum ada solusi yang jelas untuk melakukan ini dengan benar ketika menggunakan async / menunggu dalam metode tanda tangan async void.

Namun, saya perhatikan bahwa. Tunggu () berfungsi dengan baik di dalam metode void.

dan karena async void dan void memiliki tanda tangan yang sama, Anda mungkin perlu melakukan hal berikut.

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

Async / wait yang cukup membingungkan tidak menghalangi pada kode selanjutnya.

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

Ketika Anda mendekompilasi kode Anda, tebakan saya adalah bahwa async void membuat Tugas internal (seperti Tugas async), tetapi karena tanda tangan tidak mendukung untuk mengembalikan tugas internal tersebut

ini berarti bahwa secara internal metode void async masih akan dapat "menunggu" metode async internal. tetapi secara eksternal tidak dapat mengetahui kapan tugas internal selesai.

Jadi kesimpulan saya adalah bahwa async void berfungsi sebagaimana mestinya, dan jika Anda memerlukan umpan balik dari Tugas internal, maka Anda harus menggunakan tanda tangan Tugas async sebagai gantinya.

semoga obrolan saya masuk akal bagi siapa pun yang juga mencari jawaban.

Sunting: Saya membuat beberapa contoh kode dan mendekompilasi untuk melihat apa yang sebenarnya terjadi.

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

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

Berubah menjadi (sunting: Saya tahu bahwa kode bodi tidak ada di sini tetapi di statemachine, tetapi statemachine pada dasarnya identik, jadi saya tidak repot menambahkannya)

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;
}

AsyncVoidMethodBuilder atau AsyncTaskMethodBuilder tidak benar-benar memiliki kode apa pun dalam metode Mulai yang akan mengisyaratkan mereka untuk memblokir, dan akan selalu berjalan secara tidak sinkron setelah dijalankan.

artinya tanpa Tugas kembali, tidak akan ada cara untuk memeriksa apakah sudah selesai.

seperti yang diharapkan, ia hanya memulai Task yang menjalankan async, dan kemudian melanjutkan dalam kode. dan Tugas async, pertama memulai Tugas, dan kemudian mengembalikannya.

jadi saya kira jawaban saya adalah untuk tidak pernah menggunakan async void, jika Anda perlu tahu kapan tugas itu selesai, itulah tugas Async.

Droa
sumber