Zaman uyumsuz dosya erişimi (C#)

Dosyalara erişmek için zaman uyumsuz özelliğini kullanabilirsiniz. Zaman uyumsuz özelliği kullanarak, geri çağırmaları kullanmadan veya kodunuzu birden çok yönteme veya lambda ifadesine bölmeden zaman uyumsuz yöntemlere çağrı yapabilirsiniz. Zaman uyumlu kodu zaman uyumsuz hale getirmek için, zaman uyumlu bir yöntem yerine zaman uyumsuz bir yöntemi çağırmanız ve koda birkaç anahtar sözcük eklemeniz gerekir.

Dosya erişim çağrılarına zaman uyumsuz eklemenin aşağıdaki nedenlerini göz önünde bulundurabilirsiniz:

  • İşlemi başlatan kullanıcı arabirimi iş parçacığı başka işler gerçekleştirebileceğinden zaman uyumsuz olması ui uygulamalarını daha duyarlı hale getirir. Ui iş parçacığının uzun süren bir kod yürütmesi gerekiyorsa (örneğin, 50 milisaniyeden fazla), G/Ç tamamlanana kadar kullanıcı arabirimi donabilir ve kullanıcı arabirimi iş parçacığı klavye ve fare girişini ve diğer olayları yeniden işleyebilir.
  • Zaman uyumsuzluğu, iş parçacığı gereksinimini azaltarak ASP.NET ve diğer sunucu tabanlı uygulamaların ölçeklenebilirliğini artırır. Uygulama yanıt başına ayrılmış bir iş parçacığı kullanıyorsa ve aynı anda bin istek işleniyorsa, bin iş parçacığı gerekir. Zaman uyumsuz işlemlerin genellikle bekleme sırasında iş parçacığı kullanması gerekmez. Sonunda mevcut G/Ç tamamlama iş parçacığını kısa bir süre kullanırlar.
  • Geçerli koşullarda dosya erişim işleminin gecikme süresi çok düşük olabilir, ancak gelecekte gecikme süresi büyük ölçüde artabilir. Örneğin, bir dosya dünyanın her yanındaki bir sunucuya taşınabilir.
  • Zaman Uyumsuz özelliğini kullanmanın ek yükü azdır.
  • Zaman uyumsuz görevler kolayca paralel olarak çalıştırılabilir.

Uygun sınıfları kullanma

Bu konudaki basit örneklerde ve File.ReadAllTextAsyncgösterilmektedirFile.WriteAllTextAsync. Dosya G/Ç işlemleri üzerinde ayrıntılı denetim için, işletim sistemi düzeyinde zaman uyumsuz G/Ç'nin oluşmasına neden olan bir seçeneği olan sınıfını kullanın FileStream . Bu seçeneği kullanarak, birçok durumda bir iş parçacığı havuzu iş parçacığını engellemekten kaçınabilirsiniz. Bu seçeneği etkinleştirmek için oluşturucu çağrısında useAsync=true veya options=FileOptions.Asynchronous bağımsız değişkenini belirtirsiniz.

Bir dosya yolu belirterek bu seçeneği ve StreamWriter ile StreamReader doğrudan açarsanız kullanamazsınız. Ancak, sınıfın açtığı bir StreamFileStream sağlarsanız bu seçeneği kullanabilirsiniz. Bekleme sırasında kullanıcı arabirimi iş parçacığı engellenmediğinden, bir iş parçacığı havuzu iş parçacığı engellenmiş olsa bile kullanıcı arabirimi uygulamalarında zaman uyumsuz çağrılar daha hızlıdır.

Metin yazma

Aşağıdaki örnekler bir dosyaya metin yazar. Her await deyiminde yöntemi hemen çıkar. Dosya G/Ç tamamlandığında, yöntem await deyimini izleyen deyimde devam eder. Zaman uyumsuz değiştirici, await deyimini kullanan yöntemlerin tanımındadır.

Basit örnek

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

    await File.WriteAllTextAsync(filePath, text);
}

Sonlu denetim örneği

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

Özgün örnekte, aşağıdaki iki deyimin daralması olan deyimi await sourceStream.WriteAsync(encodedText, 0, encodedText.Length);bulunur:

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

İlk deyim bir görev döndürür ve dosya işlemenin başlatılmasına neden olur. await ile ikinci deyimi, yönteminin hemen çıkıp farklı bir görev döndürmesine neden olur. Dosya işleme daha sonra tamamlandığında yürütme, await öğesini izleyen deyimine döner.

Metni okuma

Aşağıdaki örneklerde dosyadaki metinler okunur.

Basit örnek

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

    Console.WriteLine(text);
}

Sonlu denetim örneği

Metin arabelleğe alınır ve bu durumda içine StringBuilderyerleştirilir. Önceki örnektekinden farklı olarak await değerinin değerlendirilmesi bir değer üretir. ReadAsync yöntemi birInt32<>Task döndürür, bu nedenle await değerlendirmesi işlem tamamlandıktan sonra bir Int32 değer numRead üretir. Daha fazla bilgi için bkz. Zaman Uyumsuz Dönüş Türleri (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();
}

Paralel zaman uyumsuz G/Ç

Aşağıdaki örneklerde 10 metin dosyası yazarak paralel işleme gösterilmektedir.

Basit örnek

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

Sonlu denetim örneği

Her dosya için WriteAsync yöntemi, daha sonra bir görev listesine eklenen bir görevi döndürür. deyimi await Task.WhenAll(tasks); yönteminden çıkar ve tüm görevler için dosya işleme tamamlandığında yöntemi içinde devam eder.

Örnek, görevler tamamlandıktan sonra bir finally bloktaki tüm FileStream örnekleri kapatır. Her FileStream biri bir using deyimde oluşturulduysa, FileStream görev tamamlanmadan önce atılabilir.

Tüm performans artışları neredeyse tamamen paralel işlemeden kaynaklanır ve zaman uyumsuz işlemeden kaynaklanmaz. Zaman uyumsuzun avantajları, birden çok iş parçacığını bağlamaması ve kullanıcı arabirimi iş parçacığını bağlamamasıdır.

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

ve ReadAsync yöntemlerini kullanırken, akışın WriteAsync ortasında işlemi iptal etmek için kullanabileceğiniz bir CancellationTokenbelirtebilirsiniz. Daha fazla bilgi için bkz. Yönetilen iş parçacıklarında iptal etme.

Ayrıca bkz.