web-dev-qa-db-fra.com

attendre les travaux mais en appelant tâche.Résultation bloque / impasses

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é?

113
Johan Larsson

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.

80
Stephen Cleary

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.

198
Herman Schoenfeld

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

16
Vladimir

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.

8
Dark Knight

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

2
Mayank Pandit