Premiers pas avec l'attente asynchrone

vide asynchrone

Vous pouvez utiliser void (au lieu de Task) comme type de retour d’une méthode asynchrone. Cela se traduira par une action “fire-and-forget”:

public void DoStuff()
{
    FireAndForgetAsync();
}
    
private async void FireAndForgetAsync()
{
    await Task.Delay(1000);
    throw new Exception(); //will be swallowed
}

Comme vous retournez void, vous ne pouvez pas attendre FireAndForgetAsync. Vous ne pourrez pas savoir quand la méthode se termine, et toute exception déclenchée à l’intérieur de la méthode async void sera avalée.

utilisation simple

Trois choses sont nécessaires pour utiliser async-await :

  • L’objet Task : Cet objet est retourné par une méthode qui s’exécute de manière asynchrone. Il permet de contrôler l’exécution de la méthode.
  • Le mot-clé await : “Attend” une Tâche. Placez ce mot-clé avant Task pour attendre de façon asynchrone qu’il se termine
  • Le mot-clé async : toutes les méthodes qui utilisent le mot-clé wait doivent être marquées comme async

Un petit exemple qui montre l’utilisation de ces mots-clés

public async Task DoStuffAsync()
{
    var result = await DownloadFromWebpageAsync(); //calls method and waits till execution finished
    var task = WriteTextAsync(@"temp.txt", result); //starts saving the string to a file, continues execution right await
    Debug.Write("this is executed parallel with WriteTextAsync!"); //executed parallel with WriteTextAsync!
    await task; //wait for WriteTextAsync to finish execution
}

private async Task<string> DownloadFromWebpageAsync()
{
    using (var client = new WebClient())
    {
        return await client.DownloadStringTaskAsync(new Uri("http://stackoverflow.com"));
    }
}

private async Task WriteTextAsync(string filePath, string text)
{
    byte[] encodedText = Encoding.Unicode.GetBytes(text);

    using (FileStream sourceStream = new FileStream(filePath, FileMode.Append))
    {
        await sourceStream.WriteAsync(encodedText, 0, encodedText.Length);
    }
} 

Certaines choses à noter :

  • Vous pouvez spécifier une valeur de retour à partir d’une opération asynchrone avec Task<string> ou similaire. Le mot-clé wait attend la fin de l’exécution de la méthode et renvoie la string.
  • l’objet Task contient simplement le statut de l’exécution de la méthode, il peut être utilisé comme n’importe quelle autre variable.
  • si une exception est levée (par exemple par le WebClient) elle bouillonne à la première utilisation du mot-clé wait (dans cet exemple à la ligne var result (...))
  • Il est recommandé de nommer les méthodes qui renvoient l’objet Task comme MethodNameAsync

exécuter du code synchrone asynchrone

Si vous souhaitez exécuter du code synchrone asynchrone (par exemple, des calculs intensifs du processeur), vous pouvez utiliser Task.Run (() => {}).

public async Task DoStuffAsync()
{
    await DoCpuBoundWorkAsync();
}


private async Task DoCpuBoundWorkAsync()
{
    await Task.Run(() =>
    {
        for (long i = 0; i < Int32.MaxValue; i++)
        {
            i = i ^ 2;
        }
    });
}

l’objet Tâche

L’objet Task est un objet comme les autres si vous enlevez les mots clés async-await.

Considérez cet exemple :

public async Task DoStuffAsync()
{
    await WaitAsync();
    await WaitDirectlyAsync();
}

private async Task WaitAsync()
{
    await Task.Delay(1000);
}

private Task WaitDirectlyAsync()
{
    return Task.Delay(1000);
}

La différence entre ces deux méthodes est simple :

  • WaitAsync attend que Task.Delay se termine, puis revient.
  • WaitDirectlyAsync n’attend pas et renvoie simplement l’objet Task instantanément.

Chaque fois que vous utilisez le mot clé wait, le compilateur génère du code pour le gérer (ainsi que l’objet Task qu’il attend).

  • Lors de l’appel de wait WaitAsync(), cela se produit deux fois : une fois dans la méthode d’appel et une fois dans la méthode elle-même.
  • Lors de l’appel de wait WaitDirectlyAsync, cela ne se produit qu’une seule fois (dans la méthode d’appel). Vous archiveriez donc un peu d’accélération par rapport à wait WaitAsync().

Attention aux exceptions : les Exceptions apparaîtront la première fois qu’une Tâche est attendue. Exemple:

private async Task WaitAsync()
{
    try
    {
        await Task.Delay(1000);
    }
    catch (Exception ex)
    {
        //this might execute
        throw;
    }
}

private Task WaitDirectlyAsync()
{
    try
    {
        return Task.Delay(1000);
    }
    catch (Exception ex)
    {
        //this code will never execute!
        throw;
    }
}