Dela via


Asynkron filåtkomst (C#)

Du kan använda funktionen async för att komma åt filer. Med hjälp av funktionen async kan du anropa asynkrona metoder utan att använda återanrop eller dela upp koden mellan flera metoder eller lambda-uttryck. Om du vill göra synkron kod asynkron anropar du bara en asynkron metod i stället för en synkron metod och lägger till några nyckelord i koden.

Du kan överväga följande orsaker till att lägga till asynkron i filåtkomstanrop:

  • Asynkron gör UI-program mer responsiva eftersom användargränssnittstråden som startar åtgärden kan utföra annat arbete. Om UI-tråden måste köra kod som tar lång tid (till exempel mer än 50 millisekunder) kan användargränssnittet låsas tills I/O har slutförts och användargränssnittstråden kan bearbeta tangentbords- och musindata och andra händelser igen.
  • Asynkronisering förbättrar skalbarheten för ASP.NET och andra serverbaserade program genom att minska behovet av trådar. Om programmet använder en dedikerad tråd per svar och tusen begäranden hanteras samtidigt behövs tusentals trådar. Asynkrona åtgärder behöver ofta inte använda en tråd under väntetiden. De använder den befintliga I/O-slutförandetråden kort i slutet.
  • Svarstiden för en filåtkomståtgärd kan vara mycket låg under aktuella förhållanden, men svarstiden kan öka avsevärt i framtiden. En fil kan till exempel flyttas till en server som finns över hela världen.
  • Den extra kostnaden för att använda Async-funktionen är liten.
  • Asynkrona uppgifter kan enkelt köras parallellt.

Använda lämpliga klasser

De enkla exemplen i det här avsnittet visar File.WriteAllTextAsync och File.ReadAllTextAsync. För fin kontroll över fil-I/O-åtgärder använder du FileStream -klassen, som har ett alternativ som gör att asynkron I/O inträffar på operativsystemnivå. Med det här alternativet kan du undvika att blockera en trådpoolstråd i många fall. Om du vill aktivera det här alternativet anger useAsync=true du argumentet eller options=FileOptions.Asynchronous i konstruktoranropet.

Du kan inte använda det här alternativet med StreamReader och StreamWriter om du öppnar dem direkt genom att ange en filsökväg. Du kan dock använda det här alternativet om du anger ett Stream som FileStream klassen har öppnat. Asynkrona anrop går snabbare i användargränssnittsappar även om en trådpoolstråd blockeras, eftersom användargränssnittstråden inte blockeras under väntetiden.

Skriva text

I följande exempel skrivs text till en fil. Vid varje await-instruktion avslutas metoden omedelbart. När filens I/O är klar återupptas metoden vid -instruktionen som följer await-instruktionen. Asynkron modifierare finns i definitionen av metoder som använder await-instruktionen.

Enkelt exempel

public async Task SimpleWriteAsync()
{
    string filePath = "simple.txt";
    string text = $"Hello World";

    await File.WriteAllTextAsync(filePath, text);
}

Finita kontrollexempel

public async Task ProcessWriteAsync()
{
    string filePath = "temp.txt";
    string text = $"Hello World{Environment.NewLine}";

    await WriteTextAsync(filePath, text);
}

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

    using var sourceStream =
        new FileStream(
            filePath,
            FileMode.Create, FileAccess.Write, FileShare.None,
            bufferSize: 4096, useAsync: true);

    await sourceStream.WriteAsync(encodedText, 0, encodedText.Length);
}

Det ursprungliga exemplet har -instruktionen await sourceStream.WriteAsync(encodedText, 0, encodedText.Length);, som är en sammandragning av följande två -uttryck:

Task theTask = sourceStream.WriteAsync(encodedText, 0, encodedText.Length);
await theTask;

Den första instruktionen returnerar en uppgift och gör att filbearbetningen startar. Den andra instruktionen med await gör att metoden omedelbart avslutas och returnerar en annan aktivitet. När filbearbetningen senare slutförs återgår körningen till -instruktionen som följer på inväntningen.

Läsa text

Följande exempel läser text från en fil.

Enkelt exempel

public async Task SimpleReadAsync()
{
    string filePath = "simple.txt";
    string text = await File.ReadAllTextAsync(filePath);

    Console.WriteLine(text);
}

Finita kontrollexempel

Texten buffras och placeras i det här fallet i en StringBuilder. Till skillnad från i föregående exempel genererar utvärderingen av await ett värde. Metoden ReadAsync returnerar en Task<>Int32, så utvärderingen av await genererar ett Int32 värde numRead när åtgärden har slutförts. Mer information finns i Async Return Types (C#).

public async Task ProcessReadAsync()
{
    try
    {
        string filePath = "temp.txt";
        if (File.Exists(filePath) != false)
        {
            string text = await ReadTextAsync(filePath);
            Console.WriteLine(text);
        }
        else
        {
            Console.WriteLine($"file not found: {filePath}");
        }
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.Message);
    }
}

async Task<string> ReadTextAsync(string filePath)
{
    using var sourceStream =
        new FileStream(
            filePath,
            FileMode.Open, FileAccess.Read, FileShare.Read,
            bufferSize: 4096, useAsync: true);

    var sb = new StringBuilder();

    byte[] buffer = new byte[0x1000];
    int numRead;
    while ((numRead = await sourceStream.ReadAsync(buffer, 0, buffer.Length)) != 0)
    {
        string text = Encoding.Unicode.GetString(buffer, 0, numRead);
        sb.Append(text);
    }

    return sb.ToString();
}

Parallell asynkron I/O

Följande exempel visar parallell bearbetning genom att skriva 10 textfiler.

Enkelt exempel

public async Task SimpleParallelWriteAsync()
{
    string folder = Directory.CreateDirectory("tempfolder").Name;
    IList<Task> writeTaskList = new List<Task>();

    for (int index = 11; index <= 20; ++ index)
    {
        string fileName = $"file-{index:00}.txt";
        string filePath = $"{folder}/{fileName}";
        string text = $"In file {index}{Environment.NewLine}";

        writeTaskList.Add(File.WriteAllTextAsync(filePath, text));
    }

    await Task.WhenAll(writeTaskList);
}

Finita kontrollexempel

För varje fil WriteAsync returnerar metoden en aktivitet som sedan läggs till i en lista över aktiviteter. - await Task.WhenAll(tasks); instruktionen avslutar metoden och återupptas i metoden när filbearbetningen är klar för alla aktiviteter.

Exemplet stänger alla FileStream instanser i ett finally block när aktiviteterna har slutförts. Om var och FileStream en i stället skapades i en using -instruktion kan den FileStream tas bort innan aktiviteten slutfördes.

Prestandaökningar är nästan helt från parallell bearbetning och inte asynkron bearbetning. Fördelarna med asynkron är att den inte binder ihop flera trådar och att den inte binder ihop användargränssnittstråden.

public async Task ProcessMultipleWritesAsync()
{
    IList<FileStream> sourceStreams = new List<FileStream>();

    try
    {
        string folder = Directory.CreateDirectory("tempfolder").Name;
        IList<Task> writeTaskList = new List<Task>();

        for (int index = 1; index <= 10; ++ index)
        {
            string fileName = $"file-{index:00}.txt";
            string filePath = $"{folder}/{fileName}";

            string text = $"In file {index}{Environment.NewLine}";
            byte[] encodedText = Encoding.Unicode.GetBytes(text);

            var sourceStream =
                new FileStream(
                    filePath,
                    FileMode.Create, FileAccess.Write, FileShare.None,
                    bufferSize: 4096, useAsync: true);

            Task writeTask = sourceStream.WriteAsync(encodedText, 0, encodedText.Length);
            sourceStreams.Add(sourceStream);

            writeTaskList.Add(writeTask);
        }

        await Task.WhenAll(writeTaskList);
    }
    finally
    {
        foreach (FileStream sourceStream in sourceStreams)
        {
            sourceStream.Close();
        }
    }
}

När du använder WriteAsync metoderna och ReadAsync kan du ange en CancellationToken, som du kan använda för att avbryta åtgärden mitt i strömmen. Mer information finns i Annullering i hanterade trådar.

Se även