- Asenkron kodun neden gerektiğini açıklamak
- Bir
async Task<T>metodu yazmak ve onuawaitile beklemek - Birden fazla görevi
Task.WhenAllile 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
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.
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!";
}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.
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";
}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:
- 1Veri modeli
Öğrenciyi tek satırlık
record Student(string Name, int Score);ile tanımlıyoruz. - 2Asenkron yükleme
LoadLinesAsyncmetodu satırları döndürür. Burada dosyayıTask.Delayile taklit ediyoruz; gerçek bir programdaawait File.ReadAllLinesAsync("scores.txt")yazılırdı. - 3Doğrulama
Her satırı
Split(';')ile parçalara ayırıyor, puanıint.TryParseile kontrol ediyoruz; hatalı bir satır programı durdurmuyor. - 4Analiz ve rapor
LINQ ile sıralıyor ve ortalamayı buluyor, switch ifadesiyle not veriyor, interpolasyonla düzgün bir tablo yazdırıyoruz.
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);Skipped bad line: 'Leyla;abc' Loaded 4 students
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);Aysel 95 A Nigar 91 A Murad 79 C Elvin 45 F Average: 77.5 Passed: 3 of 4
İ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.
asyncbir metotTaskya daTask<T>döndürür ve adıAsyncile biter.awaitbir görevin sonucunu bekler; unutulursaCS4014uyarısı çıkar.Task.WhenAllbirkaç 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.
Task<int> neyi temsil eder?