J'ai les quatre tests suivants et le dernier se bloque lorsque je l'exécute, ma question est de savoir pourquoi cela se produit:
[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;
}
J'utilise cette méthode d'extension pour 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; }
}
Pourquoi le dernier test est-il bloqué?
Vous rencontrez la situation standard de blocage que je décris sur mon blog et dans un article MSDN : la méthode async
tente de planifier sa continuation sur un thread bloqué par l'appel de Result
.
Dans ce cas, votre SynchronizationContext
est celui utilisé par NUnit pour exécuter async void
Méthodes d'essai. Je voudrais essayer d'utiliser async Task
méthodes de test à la place.
Acquisition d'une valeur via une méthode asynchrone:
var result = Task.Run(() => asyncGetValue()).Result;
Appeler de manière synchrone une méthode asynchrone
Task.Run( () => asyncMethod()).Wait();
Aucun problème d'interblocage ne se produira en raison de l'utilisation de Task.Run.
Vous pouvez éviter un blocage en ajoutant ConfigureAwait(false)
à cette ligne:
IRestResponse<DummyServiceStatus> response = await restResponse;
=>
IRestResponse<DummyServiceStatus> response = await restResponse.ConfigureAwait(false);
J'ai décrit ce piège dans mon article de blog pièges async/wait
Vous bloquez l'interface utilisateur à l'aide de la propriété Task.Result. Dans MSDN Documentation ils ont clairement mentionné que,
"La propriété Result est une propriété bloquante. Si vous essayez d'y accéder avant la fin de sa tâche, le thread actuellement actif est bloqué jusqu'à la fin de la tâche. et la valeur est disponible Dans la plupart des cas, vous devez accéder à la valeur en utilisant Await ou wait au lieu d’accéder directement à la propriété. "
La meilleure solution pour ce scénario serait de supprimer les deux wait & async des méthodes et d’utiliser uniquement Tâche Où vous retournez le résultat. Cela ne va pas gâcher votre séquence d'exécution.
Si vous ne recevez aucun rappel ou que le contrôle ne se bloque pas, après avoir appelé la fonction asynchrone Service/API.
Vous devez configurer le contexte pour renvoyer le résultat sur le même contexte appelé. Utilisez TestAsync().ConfigureAwait(continueOnCapturedContext: false);
Vous ne rencontrerez ce problème que dans les applications Web, mais pas dans Static void main