async-awaitAan de slag met async-await


Opmerkingen

async-await maakt asynchrone (betekent niet-blokkerende, parallelle) uitvoering van code mogelijk. Het helpt om uw UI te allen tijde responsief te houden, terwijl potentieel lange operaties op de achtergrond worden uitgevoerd.

Het is vooral handig voor I / O-bewerkingen (zoals downloaden van een server of het lezen van een bestand van de HDD), maar het kan ook worden gebruikt om CPU-intensieve berekeningen uit te voeren zonder uw toepassing te bevriezen.

async nietig

U kunt void (in plaats van Task ) gebruiken als een retourtype van een asynchrone methode. Dit resulteert in een "vuur-en-vergeet" -actie:

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

Omdat je void , kun je niet await FireAndForgetAsync . U zult niet in staat zijn om te weten wanneer de methode is voltooid en elke uitzondering die binnen de async void methode wordt opgeworpen, wordt ingeslikt.

synchrone code asynchroon uitvoeren

Als u asynchrone synchrone code wilt uitvoeren (bijvoorbeeld CPU-uitgebreide berekeningen), kunt u 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;
        }
    });
}
 

eenvoudig gebruik

Er zijn drie dingen nodig om async-await :

  • Het Task : dit object wordt geretourneerd door een methode die asynchroon wordt uitgevoerd. Hiermee kunt u de uitvoering van de methode regelen.
  • Het await trefwoord: "wacht op" een Task . Plaats dit trefwoord vóór de Task om asynchroon te wachten tot het klaar is
  • Het async sleutelwoord: alle methoden die het await sleutelwoord gebruiken, moeten als async worden gemarkeerd

Een klein voorbeeld dat het gebruik van deze zoekwoorden laat zien

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);
    }
} 
 

Enkele aandachtspunten:

  • U kunt een retourwaarde van asynchrone bewerkingen opgeven met Task<string> of iets dergelijks. De await zoekwoord wacht tot de uitvoering van de methode is voltooid en keert de string .
  • het Task bevat eenvoudig de status van de uitvoering van de methode, het kan als elke andere variabele worden gebruikt.
  • Als een uitzondering gegenereerd (bijvoorbeeld door WebClient ) borrelt bij de eerste keer de await sleutelwoord wordt gebruikt (in dit voorbeeld op de lijn var result (...) )
  • Aanbevolen wordt naam methoden die de terugkeer Task object als MethodNameAsync

het taakobject

Het Task is een object zoals elk ander als u de async-await zoekwoorden async-await .

Beschouw dit voorbeeld:

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

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

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

Het verschil tussen deze twee methoden is eenvoudig:

  • WaitAsync wacht op Task.Delay om te voltooien en keert vervolgens terug.
  • WaitDirectlyAsync wacht niet, en geeft alleen de Task object direct.

Telkens wanneer u het sleutelwoord await , genereert de compiler code om ermee om te gaan (en het Task het wacht).

  • await WaitAsync() bij het await WaitAsync() dit gebeurt twee keer: eenmaal in de await WaitAsync() en eenmaal in de methode zelf.
  • await WaitDirectlyAsync bij het await WaitDirectlyAsync gebeurt dit slechts eenmaal (in de await WaitDirectlyAsync ). Je zou daarom een beetje versnelling archiveren vergeleken met await WaitAsync() .

Pas op met exceptions : Exceptions zullen de eerste keer dat een Task await . Voorbeeld:

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;
    }
}