Saya memiliki empat tes berikut dan yang terakhir hang saat saya menjalankannya. Mengapa ini terjadi:
[Test]
public void CheckOnceResultTest()
{
Assert.IsTrue(CheckStatus().Result);
}
[Test]
public async void CheckOnceAwaitTest()
{
Assert.IsTrue(await CheckStatus());
}
[Test]
public async void CheckStatusTwiceAwaitTest()
{
Assert.IsTrue(await CheckStatus());
Assert.IsTrue(await CheckStatus());
}
[Test]
public async void CheckStatusTwiceResultTest()
{
Assert.IsTrue(CheckStatus().Result); // This hangs
Assert.IsTrue(await CheckStatus());
}
private async Task<bool> CheckStatus()
{
var restClient = new RestClient(@"https://api.test.nordnet.se/next/1");
Task<IRestResponse<DummyServiceStatus>> restResponse = restClient.ExecuteTaskAsync<DummyServiceStatus>(new RestRequest(Method.GET));
IRestResponse<DummyServiceStatus> response = await restResponse;
return response.Data.SystemRunning;
}
Saya menggunakan metode ekstensi ini untuk restsharp RestClient :
public static class RestClientExt
{
public static Task<IRestResponse<T>> ExecuteTaskAsync<T>(this RestClient client, IRestRequest request) where T : new()
{
var tcs = new TaskCompletionSource<IRestResponse<T>>();
RestRequestAsyncHandle asyncHandle = client.ExecuteAsync<T>(request, tcs.SetResult);
return tcs.Task;
}
}
public class DummyServiceStatus
{
public string Message { get; set; }
public bool ValidVersion { get; set; }
public bool SystemRunning { get; set; }
public bool SkipPhrase { get; set; }
public long Timestamp { get; set; }
}
Mengapa tes terakhir hang?
async void
metode pengujian unit sama sekali; mereka tidak akan berhasil. Namun, NUnit melakukannya. Yang mengatakan, saya setuju dengan prinsip umum lebih memilihasync Task
lebihasync void
.Jawaban:
Anda mengalami situasi kebuntuan standar yang saya jelaskan di blog saya dan dalam artikel MSDN :
async
metode ini mencoba menjadwalkan kelanjutannya ke utas yang diblokir oleh panggilan keResult
.Dalam hal ini, Anda
SynchronizationContext
adalah yang digunakan oleh NUnit untuk menjalankanasync void
metode pengujian. Saya akan mencoba menggunakanasync Task
metode pengujian sebagai gantinya.sumber
async
sepenuhnya" (seperti yang disebutkan dalam artikel MSDN saya). Dengan kata lain - seperti yang dinyatakan dalam judul postingan blog saya - "jangan blokir kode asinkron".Wait()
dengan membuat metode panggilanasync
. Tapi bagi saya, ini tampaknya mendorong masalah ke hulu. Pada titik tertentu, sesuatu harus dikelola secara sinkron. Bagaimana jika fungsi saya sengaja disinkronkan karena mengelola thread pekerja yang berjalan lamaTask.Run()
? Bagaimana saya menunggu sampai selesai tanpa jalan buntu di dalam tes NUnit saya?At some point, something has to be managed synchronously.
- tidak sama sekali. Untuk aplikasi UI, entrypoint bisa menjadiasync void
pengendali kejadian. Untuk aplikasi server, entrypoint bisa berupaasync Task<T>
tindakan. Lebih baik menggunakanasync
keduanya untuk menghindari pemblokiran utas. Anda dapat membuat pengujian NUnit Anda sinkron atau asinkron; jika asinkron, jadikan sebagaiasync Task
gantinyaasync void
. Jika sinkron, seharusnya tidakSynchronizationContext
ada jadi seharusnya tidak ada kebuntuan.Mendapatkan nilai melalui metode async:
Memanggil metode asinkron secara sinkron
Tidak ada masalah kebuntuan yang akan terjadi karena penggunaan Task.Run.
sumber
async void
metode pengujian unit dan menghapus jaminan thread yang sama yang disediakan olehSynchronizationContext
dari sistem yang diuji..GetAwaiter().GetResult()
bukannya.Result
sehingga setiapException
tidak dibungkus.Anda dapat menghindari kebuntuan menambah
ConfigureAwait(false)
baris ini:=>
Saya telah menjelaskan jebakan ini di posting blog saya Pitfalls of async / await
sumber
Anda memblokir UI dengan menggunakan properti Task.Result. Dalam Dokumentasi MSDN mereka dengan jelas menyebutkan bahwa,
Solusi terbaik untuk skenario ini adalah menghapus await & async dari metode & hanya menggunakan Tugas tempat Anda mengembalikan hasil. Itu tidak akan mengacaukan urutan eksekusi Anda.
sumber
Jika Anda tidak mendapatkan callback atau kontrol berhenti, setelah memanggil fungsi async service / API, Anda harus mengonfigurasi Context untuk mengembalikan hasil pada konteks yang disebut sama.
Menggunakan
TestAsync().ConfigureAwait(continueOnCapturedContext: false);
Anda akan menghadapi masalah ini hanya di aplikasi web, tetapi tidak di
static void main
.sumber
ConfigureAwait
menghindari kebuntuan dalam skenario tertentu dengan tidak berjalan dalam konteks utas asli.