J'ai les quatre tests suivants et le dernier se bloque lorsque je l'exécute. Pourquoi cela arrive-t-il:
[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 se bloque-t-il?
async void
les méthodes de test unitaire; ils ne fonctionneront tout simplement pas. Cependant, NUnit le fait. Cela dit, je suis d' accord avec le principe général de préférerasync Task
plusasync void
.Réponses:
Vous vous heurtez à la situation de blocage standard que je décris sur mon blog et dans un article MSDN : la
async
méthode tente de planifier sa poursuite sur un thread bloqué par l'appel àResult
.Dans ce cas, votre
SynchronizationContext
est celui utilisé par NUnit pour exécuterasync void
les méthodes de test. J'essaieraisasync Task
plutôt d' utiliser des méthodes de test.la source
async
tout le chemin" (comme indiqué dans mon article MSDN). En d'autres termes - comme le titre de mon article de blog l'indique - "ne bloquez pas le code asynchrone".Wait()
par la méthode d'appelasync
. Mais pour moi, cela semble pousser le problème en amont. À un moment donné, quelque chose doit être géré de manière synchrone. Que faire si ma fonction est volontairement synchrone car elle gère des threads de travail de longue durée avecTask.Run()
? Comment attendre que cela se termine sans interblocage dans mon test NUnit?At some point, something has to be managed synchronously.
- pas du tout. Pour les applications d'interface utilisateur, le point d'entrée peut être unasync void
gestionnaire d'événements. Pour les applications serveur, le point d'entrée peut être uneasync Task<T>
action. Il est préférable d'utiliser lesasync
deux pour éviter de bloquer les threads. Vous pouvez faire en sorte que votre test NUnit soit synchrone ou asynchrone; si asynchrone, faites-leasync Task
au lieu deasync void
. S'il est synchrone, il ne devrait pasSynchronizationContext
y avoir de blocage donc il ne devrait pas y avoir de blocage.Acquérir une valeur via une méthode asynchrone:
Appel synchrone d'une méthode asynchrone
Aucun problème de blocage ne se produira en raison de l'utilisation de Task.Run.
la source
async void
méthodes de test unitaire et supprimer les garanties de même thread fournies par leSynchronizationContext
du système testé..GetAwaiter().GetResult()
au lieu de.Result
pour qu'aucunException
ne soit emballé.Vous pouvez éviter un blocage en ajoutant
ConfigureAwait(false)
à cette ligne:=>
J'ai décrit cet écueil dans mon article de blog Pitfalls of async / await
la source
Vous bloquez l'interface utilisateur à l'aide de la propriété Task.Result. Dans la documentation MSDN, ils ont clairement mentionné que,
La meilleure solution pour ce scénario serait de supprimer à la fois wait et async des méthodes et d'utiliser uniquement la tâche où vous renvoyez le résultat. Cela ne gâchera pas votre séquence d'exécution.
la source
Si vous n'obtenez aucun rappel ou si le contrôle se bloque, après avoir appelé la fonction asynchrone du service / API, vous devez configurer Context pour renvoyer un résultat sur le même contexte appelé.
Utilisation
TestAsync().ConfigureAwait(continueOnCapturedContext: false);
Vous serez confronté à ce problème uniquement dans les applications Web, mais pas dans
static void main
.la source
ConfigureAwait
évite les interblocages dans certains scénarios en ne s'exécutant pas dans le contexte de thread d'origine.