CodeGym /Kurslar /C# SELF /Tekst faylları ilə asinxron işləmə

Tekst faylları ilə asinxron işləmə

C# SELF
Səviyyə, Dərs
Mövcuddur

1. Giriş

Böyük şəkilin yüklənməsi nümunəmizi xatırlayın. O yüklənənə qədər tətbiq "donur". Eyni şey tekst faylları ilə də baş verir, xüsusilə onlar böyükdürsə (onlarla gigabaytlıq loglar, nəhəng CSV-hesabatlar, verilənlər bazasının tekst formatlı backup-ları).

Təsəvvür edin ki, siz belə bir tətbiq yazırsınız:

Böyük log faylını parse edir ki, səhvləri tapsın. Əgər onu sinxron oxuyacaqsınızsa, istifadəçi interfeysi bir neçə saniyə və ya hətta dəqiqə boyunca "donacaq". İstifadəçi proqramın sıradan çıxdığını düşünə bilər.

Nəsə hesabat faylına məlumat yazır əmələ gəlmə vaxtı. Əgər yazma əsas thread-i bloklayırsa, həm məlumatın generasiyası, həm də UI ilə işləmə əziyyət çəkəcək.

Veb-server, minlərlə sorğunu servis etməlidir. Hər sorğu fayl oxumaq və ya yazmaq tələb edə bilər. Əgər hər belə fayl I/O sinxron olacaqsa, serverin threadləri diskə görə boşdayacaq və server sürətlə sorğu dalğasından "boğulacaq".

Belə ssenarilərdə asinxron I/O sadəcə "xoş funksiya" deyil, həyati ehtiyacdır. O tətbiqinizin disk "düşünərkən" boş durmamasına, faydalı işlə məşğul olmasına imkan verir (məsələn, UI-nı yeniləmək, digər sorğuları emal etmək və ya hesablama aparmaq).

Əsas anlayışlar: async/await və task-lar

  • Açar söz async göstərir ki, metod "gözləmə nöqtələri" (await) içərə bilər.
  • await operatoru asinxron task bitənə qədər idarəni müvəqqəti verir.
  • Asinxron metod cari thread-i bloklamadan I/O həyata keçirir: data olmadıqda — thread azaddır.

Bütün bunlar fayllarla asinxron işləmənin əsasıdır.

2. Fayllar üçün asinxron metodlar

Müasir .NET versiyalarında fayllarla işləyən əsas siniflərin çoxunda asinxron analoqlar mövcuddur. Tekst faylları üçün ən çox istifadə olunanlar:

  • StreamReader.ReadLineAsync()
  • StreamReader.ReadToEndAsync()
  • StreamWriter.WriteLineAsync()
  • StreamWriter.WriteAsync()
  • Həmçinin statik metodlar: File.ReadAllTextAsync(), File.WriteAllTextAsync() və s.
Oxuma Yazma
ReadLineAsync()
WriteLineAsync()
ReadToEndAsync()
WriteAsync()
File.ReadAllXAsync()
File.WriteAllXAsync()

3. Bütöv tekst faylını asinxron oxumaq

Gəlin faylı bir string kimi tam oxuyaq. Bu kiçik fayllar üçün edilir: konfiq fayllar, kiçik loglar.

using System;
using System.IO;
using System.Threading.Tasks;

class Program
{
    static async Task Main()
    {
        string path = "input.txt";
        
        // Asinxron olaraq bütün faylı oxuyuruq
        string fileContents = await File.ReadAllTextAsync(path);
        
        Console.WriteLine("Faylın məzmunu:");
        Console.WriteLine(fileContents);
    }
}

Diqqət edin: Main metodu indi async Task kimi işarələnib. Bunu C# 7.1-dən başlayaraq etmək olar. Bir await — və hər şey asinxron işləyir!

4. Böyük faylı sətir-sətir asinxron oxumaq

Fayl həqiqətən böyükdürsə, onu bütövlükdə yaddaşa yükləmək yaxşı fikir deyil. Daha yaxşısı sətir-sətir oxumaqdır:

using System;
using System.IO;
using System.Threading.Tasks;

class Program
{
    static async Task Main()
    {
        string path = "biglog.txt";

        // Asinxron oxumaq üçün StreamReader açırıq
        using StreamReader reader = new StreamReader(path);

        string? line;
        while ((line = await reader.ReadLineAsync()) != null)
        {
            // Burada sətiri emal etmək olar (məsələn, səhvləri axtarmaq)
            Console.WriteLine(line);
        }
    }
}

Bu necə işləyir?

Hər await reader.ReadLineAsync() çağırışı thread-i azad edir — bu xüsusilə fayl şəbəkə diskində və ya cloud-da olduqda faydalıdır. Asinxron emal on minlərlə sətir və istifadəçilərlə paralel işləmə üçün kritikdir (məsələn, server API-də).

5. Fayla sətirləri asinxron yazmaq

Oxşar şəkildə məlumatı fayla asinxron yazmaq olar (məsələn, hesabat generasiyası zamanı):

using System;
using System.IO;
using System.Threading.Tasks;

class Program
{
    static async Task Main()
    {
        string path = "output.txt";

        using StreamWriter writer = new StreamWriter(path);

        for (int i = 0; i < 5; i++)
        {
            await writer.WriteLineAsync($"Sətir nömrə {i + 1}");
        }
        
        // Dəqiq yazmanı təmin etmək üçün FlushAsync çağırmaq olar
        await writer.FlushAsync();

        Console.WriteLine("Məlumat asinxron yazıldı!");
    }
}

FlushAsync() çağırışı həmişə vacib deyil — StreamWriter bağlananda bufer atılacaq. Amma "indi dərhal" zəmanət lazımdırsa, istifadə edin.

6. Bir neçə asinxron fayl əməliyyatlarının qarşılıqlı təsiri

Tutaq ki, bir faylı oxuyub eyni zamanda başqa fayla çevrilmiş versiyasını yazmaq lazımdır:

using System;
using System.IO;
using System.Threading.Tasks;

class Program
{
    static async Task Main()
    {
        string sourcePath = "even_biggerlog.txt";
        string destinationPath = "copy_biggerlog.txt";

        using StreamReader reader = new StreamReader(sourcePath);
        using StreamWriter writer = new StreamWriter(destinationPath);

        string? line;
        int linesProcessed = 0;

        while ((line = await reader.ReadLineAsync()) != null)
        {
            // Bir az sehr: bütün hərfləri böyük hərflərə çeviririk
            string processed = line.ToUpperInvariant();

            await writer.WriteLineAsync(processed);
            linesProcessed++;
        }

        Console.WriteLine($"Emal olunan sətirlər: {linesProcessed}");
    }
}

Burada həm oxuma, həm yazma asinxrondur. Hər await idarəni azad edir və tətbiqin başqa iş görməsinə imkan yaradır.

7. Praktiki tətbiq: harada istifadə olunur?

  • Veb inkişafı (ASP.NET Core): fayl yükləmə/çıxarma digər sorğuların işlənməsini bloklamır; server cavabdeh qalır.
  • Desktop tətbiqlər (WPF, WinForms): log açılarkən və ya hesabat saxlanarkən UI "asılıq" keçirmir.
  • Game engine-lər: resursların (teksturalar, modellər) asinxron yüklənməsi animasiya və gameplay-i kəsmir.
  • Böyük data emalı: nəhəng CSV/JSON/XML faylları sətir-sətir parse etmək, "uçuş zamanı" emal etmək və əlavə yaddaş xərcləməmək.
  • Background xidmətlər və daemon-lar: loglama, caching, queue emalı thread və diskdən effektiv istifadə ilə.

Nəticə: asinxronluq müasir, cavabdeh və miqyaslana bilən tətbiqlər yaratmağa kömək edir. "Bloklama" pisdir, asinxronluq yaxşıdır!

8. Nüanslar və ən yaxşı təcrübələr

Unutmayın await! Əgər Async ilə bitən metodu çağırıb gözləməsəniz, nəticədə sizə Task gələcək, amma kod irəli davam edəcək və icra sırası səhvlərə gətirib çıxarar.

// PIS: await unutdunuz
FileManager.ReadTextFileAsync("nonexistent.txt"); // başlayacaq, amma Main davam edəcək
Console.WriteLine("Mən dərhal icra olundum, halbuki fayl hələ oxunur (və ya artıq səhv verdi)! Bu pisdir!");

Compiler adətən unutulmuş await barədə xəbərdarlıq verəcək, amma build-u dayandırmayacaq.

using hər şey üçün ki, IDisposabledir: bütün stream-lər (FileStream, StreamReader, StreamWriter) düzgün şəkildə azad olunmalıdır. Zəmanətli bağlanma və bufer atmaq üçün using-blokları və ya C# 8+ using deklarasiyalarından istifadə edin.

Buffer ölçüsü (bufferSize): StreamReader/StreamWriter artıq optimallaşdırılıb, amma xüsusi tələblər varsa, eksperiment etmək olar. Default dəyərlər rahatdır (FileStream-də tez-tez 4096 bayt göstərilir).

Səhv idarəsi: asinxron metodlar da istisna atır. Əməliyyatları try-catch blokunda saxlayın. İstisna müvafiq Task-də await zamanı "çölə çıxacaq".

ConfigureAwait: kitabxanalarda və web-ssenarilərdə, əgər synchronization context (GUI) lazım deyilsə, await SomeAsync().ConfigureAwait(false) istifadə edin. Bu context keçidinin overhead-ni azaldır. Konsol və UI tətbiqlərində adətən bunu keçmək olar.

Təcrübə etdikcə async Task sizə elə tanış olacaq ki, Console.WriteLine kimi rahat olacaq.

9. Asinxron fayl əməliyyatlarının tipik səhvləri və vacib nüanslar

Əgər await istifadə etməsəniz (sadəcə Async ilə bitən metodu çağırmaq), siz Task obyekti alacaqsınız, amma nəticə avtomatik gözlənilməyəcək. Onu ya await ilə gözləməlisiniz, ya da açıq şəkildə gözləməlisiniz (bu adətən məsləhət deyil).

Sinxron metoddan asinxron metodları gözləmək olmaz, əgər üst səviyyədə async etməsəniz. .Result və ya .GetAwaiter().GetResult() istifadə etmək deadlock-lara səbəb ola bilər — çağıran metodları async etmək daha düzgündür.

Eyni faylı eyni anda oxumaq və yazmaq olmaz (hətta asinxron olsa da). Bu yarışlara və data korlanmasına gətirib çıxara bilər.

Asinxronluq çağıran thread-i azad edir, amma əməliyyatları sürətləndirmir: disk və ya şəbəkə yavaşdırsa, asinxron və ya sinxron olmasından asılı olmayaraq əməliyyat eyni dərəcədə yavaş olacaq — fərq ondadır ki, UI və ya başqa işlər bloklanmır.

2
Tapşırıq
C# SELF, səviyyə, dərs
Bağlanıb
Faylı asinxron şəkildə sətr-sətr oxumaq
Faylı asinxron şəkildə sətr-sətr oxumaq
Şərhlər
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION