İçeriğe geç
Educora
İleri24 dk10 / 16

async/await ve küçük bir konsol projesi

Bekleme gerektiren işleri programı dondurmadan `Task`, `async` ve `await` ile yap; sonra kursta öğrendiklerini tek bir projede, bir not defteri raporunda birleştir.

Kendini test et
Bu derste öğreneceklerin
  • Asenkron kodun neden gerektiğini açıklamak
  • Bir async Task<T> metodu yazmak ve onu await ile beklemek
  • Birden fazla görevi Task.WhenAll ile aynı anda beklemek
  • Record'ları, koleksiyonları, LINQ'i ve doğrulamaları küçük bir programda birleştirmek

Sabah çay demliyorsun. Çaydanlık kaynayana kadar başında beklemiyorsun; bu arada ekmek kesiyor, sofrayı hazırlıyorsun. Programlar da sık sık bekler: bir dosyanın okunmasını, bir sunucunun cevabını, bir veritabanını. Program beklerken “donmasın” diye C#'ta async ve await anahtar sözcükleriyle asenkron kod yazılır.

Task, async ve await

Tanım
Task

Gelecekte tamamlanacak bir işi temsil eden nesne. Task sonuçsuz bir iştir; Task<T> ise sonunda T türünde bir sonuç verecek iştir. Örneğin bir Task<string> bir string döndürecektir.

Asenkron bir metodun başlığına async yazılır; metot Task ya da Task<T> döndürür ve adı geleneksel olarak Async ile biter. Metodun içinde await, “bu iş bitene kadar bekle ama bu arada kimseyi engelleme” demektir. Task.Delay(500), 500 milisaniyelik bir beklemeyi taklit eder; gerçek bir programda onun yerinde bir dosya okuma ya da internet isteği olurdu. Üst düzey deyimlerde await doğrudan yazılabilir.

C#
Console.WriteLine("Ordering tea...");
string tea = await MakeTeaAsync();
Console.WriteLine(tea);

async Task<string> MakeTeaAsync()
{
    Console.WriteLine("The kettle is boiling...");
    await Task.Delay(500);
    return "Tea is ready!";
}
Beklenen çıktı
Ordering tea...
The kettle is boiling...
Tea is ready!

Asenkronluğun asıl gücü, birkaç işi aynı anda beklerken ortaya çıkar. Bir metodu await olmadan çağırırsan metot başlar ve sana bir Task verir. Ardından Task.WhenAll tüm görevlerin bitmesini birlikte bekler ve sonuçlarını başlatılma sırasına göre bir dizide verir. Her biri yarım saniye süren iki iş, bir saniyede değil, yaklaşık yarım saniyede biter.

C#
using System.Diagnostics;

var timer = Stopwatch.StartNew();

Task<string> teaTask = PrepareAsync("tea", 500);
Task<string> toastTask = PrepareAsync("toast", 500);
string[] results = await Task.WhenAll(teaTask, toastTask);

Console.WriteLine(string.Join(" + ", results));
Console.WriteLine($"Faster than one by one: {timer.ElapsedMilliseconds < 1000}");

async Task<string> PrepareAsync(string item, int milliseconds)
{
    Console.WriteLine($"Started: {item}");
    await Task.Delay(milliseconds);
    return $"{item} ready";
}
Beklenen çıktı
Started: tea
Started: toast
tea ready + toast ready
Faster than one by one: True
Stopwatch geçen süreyi ölçer: iki iş birlikte bir saniyeden kısa sürdü.

Proje: not defteri raporu

Şimdi kursta öğrendiklerini birleştirelim. Bir öğretmen öğrencilerin puanlarını Ad;Puan biçiminde satırlar olarak saklıyor. Program verileri yüklemeli, hatalı satırları atlamalı, her öğrenciye bir not vermeli ve kısa bir rapor yazdırmalıdır. İşi adımlara bölüyoruz:

  1. 1
    Veri modeli

    Öğrenciyi tek satırlık record Student(string Name, int Score); ile tanımlıyoruz.

  2. 2
    Asenkron yükleme

    LoadLinesAsync metodu satırları döndürür. Burada dosyayı Task.Delay ile taklit ediyoruz; gerçek bir programda await File.ReadAllLinesAsync("scores.txt") yazılırdı.

  3. 3
    Doğrulama

    Her satırı Split(';') ile parçalara ayırıyor, puanı int.TryParse ile kontrol ediyoruz; hatalı bir satır programı durdurmuyor.

  4. 4
    Analiz ve rapor

    LINQ ile sıralıyor ve ortalamayı buluyor, switch ifadesiyle not veriyor, interpolasyonla düzgün bir tablo yazdırıyoruz.

C#
string[] lines = await LoadLinesAsync();
List<Student> students = [];

foreach (string line in lines)
{
    string[] parts = line.Split(';');
    if (parts.Length == 2 && int.TryParse(parts[1], out int score))
        students.Add(new Student(parts[0], score));
    else
        Console.WriteLine($"Skipped bad line: '{line}'");
}
Console.WriteLine($"Loaded {students.Count} students");

async Task<string[]> LoadLinesAsync()
{
    await Task.Delay(300);
    return ["Aysel;95", "Murad;79", "Leyla;abc", "Elvin;45", "Nigar;91"];
}

record Student(string Name, int Score);
Beklenen çıktı
Skipped bad line: 'Leyla;abc'
Loaded 4 students
1. bölüm: yükleme ve doğrulama
C#
using System.Globalization;

CultureInfo.CurrentCulture = CultureInfo.InvariantCulture;
List<Student> students = [new("Aysel", 95), new("Murad", 79), new("Elvin", 45), new("Nigar", 91)];

foreach (var s in students.OrderByDescending(s => s.Score))
    Console.WriteLine($"{s.Name,-6}{s.Score,4}  {Grade(s.Score)}");

Console.WriteLine($"Average: {students.Average(s => s.Score):F1}");
Console.WriteLine($"Passed: {students.Count(s => s.Score >= 50)} of {students.Count}");

string Grade(int score) => score switch
{
    >= 90 => "A",
    >= 80 => "B",
    >= 70 => "C",
    >= 50 => "D",
    _ => "F"
};

record Student(string Name, int Score);
Beklenen çıktı
Aysel   95  A
Nigar   91  A
Murad   79  C
Elvin   45  F
Average: 77.5
Passed: 3 of 4
2. bölüm: yüklenen öğrencilerin raporu

İki bölümü tek bir app.cs dosyasında birleştir: rapor kodunu yükleme kodundan sonra yaz, record Student ise dosyanın sonunda bir kez kalsın. Sonra projeyi kendin geliştir:

  • Verileri gerçek bir dosyadan oku: await File.ReadAllLinesAsync("scores.txt").
  • Console.ReadLine() ile kullanıcıdan yeni bir öğrenci eklemesini iste.
  • GroupBy(s => Grade(s.Score)) ile her nottan kaç öğrenci olduğunu hesapla.
  • Raporu await File.WriteAllTextAsync("report.txt", text) ile bir dosyaya kaydet.

Önemli noktalar

  • Asenkron kod, beklerken programı dondurmaz.
  • async bir metot Task ya da Task<T> döndürür ve adı Async ile biter.
  • await bir görevin sonucunu bekler; unutulursa CS4014 uyarısı çıkar.
  • Task.WhenAll birkaç görevi birlikte bekler ve zaman kazandırır.
  • Gerçek bir program küçük parçalardan kurulur: model (record), yükleme, doğrulama, analiz (LINQ) ve rapor.

Kendini test et

10 soru. Her doğru cevap XP kazandırır.

1 / 10
Task<int> neyi temsil eder?