Yükleniyor...

C# Asenkron Programlama Temelleri (async/await)

C#’ta async ve await kullanımını öğrenin. Asenkron işlemler, Task yapısı ve performanslı uygulama akışları örneklerle anlatılıyor.

Modern uygulamalarda kullanıcı arayüzünü dondurmadan veya uzun süren işlemleri bekletmeden çalışabilmek için asenkron programlama önemlidir. C# dilinde bu amaçla async ve await anahtar kelimeleri kullanılır. Bu yapı, uzun süren işlemleri arka planda yürütürken uygulamanın yanıt vermeye devam etmesini sağlar.


Asenkron Programlama Nedir?

Asenkron programlama, bir görevin tamamlanmasını beklerken diğer işlemlerin engellenmeden devam etmesini sağlar. Özellikle dosya okuma, ağ işlemleri, veri tabanı sorguları veya API çağrıları gibi I/O tabanlı işlemler için kullanılır.


// Senkron: işlemler sırayla çalışır
DosyaOku();
VeriGonder();
Console.WriteLine("Tamamlandı!");

// Asenkron: işlemler beklenmeden devam eder
await DosyaOkuAsync();
await VeriGonderAsync();
Console.WriteLine("Tamamlandı!");

async ve await Anahtar Kelimeleri

async anahtar kelimesi bir metodu “asenkron” hale getirir. Bu metod içinde await ifadesiyle başka asenkron işlemlerin tamamlanması beklenir. Böylece thread bloklanmadan diğer görevler çalışmaya devam eder.


async Task IslemYapAsync()
{
    Console.WriteLine("İşlem başladı...");
    await Task.Delay(2000); // 2 saniye bekletir ama thread'i durdurmaz
    Console.WriteLine("İşlem tamamlandı!");
}

Yukarıdaki örnekte Task.Delay ile zaman geciktirilse bile uygulama donmaz. Çünkü işlem, CPU’yu bloklamadan arka planda bekler.


Task ve Task<T> Tipleri

Asenkron metotlar genellikle Task veya Task<T> döndürür. Task yalnızca işlemin tamamlandığını bildirir; Task<T> ise bir sonuç döndürebilir.


// Geri dönüşsüz asenkron metot
async Task DosyaKaydetAsync(string ad)
{
    await File.WriteAllTextAsync(ad, "Veri içerik...");
}

// Sonuç döndüren asenkron metot
async Task<string> VeriGetirAsync()
{
    await Task.Delay(1000);
    return "Sunucudan gelen veri";
}

// Kullanımı:
string sonuc = await VeriGetirAsync();
Console.WriteLine(sonuc);

Asenkron Main Metodu

C# 7.1 ve sonrasında Main metodunun da asenkron olması mümkündür. Böylece konsol uygulamalarında doğrudan await kullanılabilir.


class Program
{
    static async Task Main()
    {
        Console.WriteLine("Veri alınıyor...");
        string data = await VeriGetirAsync();
        Console.WriteLine($"Sonuç: {data}");
    }

    static async Task<string> VeriGetirAsync()
    {
        await Task.Delay(1500);
        return "Tamamlandı!";
    }
}

await Kullanımında Dikkat Edilmesi Gerekenler


// Birden fazla görevi paralel çalıştırma
var islem1 = DosyaOkuAsync();
var islem2 = VeriGonderAsync();
await Task.WhenAll(islem1, islem2);
Console.WriteLine("Her iki işlem de tamamlandı.");

Asenkron Kodda Hata Yönetimi

Asenkron metotlarda hata yönetimi try-catch bloklarıyla yapılabilir. await edilen bir görev hata fırlatırsa catch bloğu çalışır.


try
{
    await DosyaOkuAsync();
}
catch (IOException ex)
{
    Console.WriteLine($"Dosya okunurken hata oluştu: {ex.Message}");
}

Uygulama: API'den Veri Çekme

Aşağıdaki örnekte HttpClient kullanarak asenkron bir HTTP isteği yapılmaktadır. await anahtar kelimesi, ağ yanıtı beklenirken uygulamanın donmasını engeller.


using System;
using System.Net.Http;
using System.Threading.Tasks;

class Program
{
    static async Task Main()
    {
        using HttpClient client = new HttpClient();
        string url = "https://jsonplaceholder.typicode.com/posts/1";
        Console.WriteLine("Veri isteniyor...");

        string json = await client.GetStringAsync(url);
        Console.WriteLine("Gelen JSON:");
        Console.WriteLine(json);
    }
}

WPF Örneği: Asenkron İşlem ve Arayüz Güncelleme

Aşağıdaki örnekte bir WPF uygulaması içinde butona tıklanınca uzun süren bir işlem (Task.Delay ile simüle edilmiştir) asenkron olarak çalıştırılır. await ifadesi sayesinde UI (arayüz) donmadan işlem devam eder ve TextBox içeriği güncellenebilir.


// MainWindow.xaml
<Window x:Class="AsyncExample.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Async / Await Örneği" Height="250" Width="400">
    <Grid Margin="20">
        <StackPanel>
            <TextBlock Text="Asenkron İşlem Örneği" FontWeight="Bold" FontSize="16" Margin="0,0,0,10"/>
            <TextBox x:Name="txtDurum" Height="30" Margin="0,0,0,10" 
                     VerticalContentAlignment="Center" FontSize="14"/>
            <Button x:Name="btnBaslat" Height="35" Content="İşlemi Başlat" 
                    Click="btnBaslat_Click" FontSize="14"/>
        </StackPanel>
    </Grid>
</Window>

// MainWindow.xaml.cs
using System;
using System.Threading.Tasks;
using System.Windows;

namespace AsyncExample
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private async void btnBaslat_Click(object sender, RoutedEventArgs e)
        {
            // async void burada event handler olduğu için uygundur
            txtDurum.Text = "İşlem başladı...";
            btnBaslat.IsEnabled = false; // buton devre dışı

            // Uzun süren bir işlem simülasyonu (örnek: dosya indirme)
            await Task.Delay(3000);

            txtDurum.Text = "İşlem tamamlandı!";
            btnBaslat.IsEnabled = true;
        }
    }
}

// Çalışma akışı:
1. Kullanıcı "İşlemi Başlat" butonuna tıklar.
2. TextBox "İşlem başladı..." şeklinde güncellenir.
3. 3 saniyelik bekleme (UI donmaz).
4. İşlem tamamlanınca TextBox "İşlem tamamlandı!" olarak değişir.

Not: Eğer bu kodu senkron biçimde (Thread.Sleep() gibi) çalıştırsaydık, UI donacak ve "İşlem başladı..." metni bile ekranda görünmeden uygulama yanıt vermeyi durduracaktı. await Task.Delay() bu sorunu tamamen ortadan kaldırır.


WPF Örneği: Task İçinden TextBox Güncelleme (Dispatcher Kullanımı)

Bu örnekte uzun süren bir işlem ayrı bir Task üzerinde çalıştırılıyor. txtDurum.Text güncellemeleri ise UI Thread’ine geri dönülerek Dispatcher.Invoke aracılığıyla yapılıyor. Böylece çoklu thread erişim hatası önleniyor.


// MainWindow.xaml
<Window x:Class="AsyncExample.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Dispatcher Örneği" Height="250" Width="400">
    <Grid Margin="20">
        <StackPanel>
            <TextBlock Text="Task İçinde UI Güncelleme" FontWeight="Bold" FontSize="16" Margin="0,0,0,10"/>
            <TextBox x:Name="txtDurum" Height="30" Margin="0,0,0,10"
                     VerticalContentAlignment="Center" FontSize="14"/>
            <Button x:Name="btnBaslat" Height="35" Content="Uzun İşlemi Başlat"
                    Click="btnBaslat_Click" FontSize="14"/>
        </StackPanel>
    </Grid>
</Window>

// MainWindow.xaml.cs
using System;
using System.Threading.Tasks;
using System.Windows;

namespace AsyncExample
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private async void btnBaslat_Click(object sender, RoutedEventArgs e)
        {
            btnBaslat.IsEnabled = false;
            txtDurum.Text = "İşlem başlatıldı...";

            // Uzun süren bir işlem Task içinde çalıştırılıyor
            await Task.Run(() =>
            {
                for (int i = 1; i <= 5; i++)
                {
                    // İşlem simülasyonu
                    Task.Delay(1000).Wait();

                    // UI güncellemesi Dispatcher ile yapılır
                    Dispatcher.Invoke(() =>
                    {
                        txtDurum.Text = $"Adım {i} tamamlandı...";
                    });
                }
            });

            txtDurum.Text = "Tüm adımlar tamamlandı!";
            btnBaslat.IsEnabled = true;
        }
    }
}

// Çalışma akışı:
1. "Uzun İşlemi Başlat" butonuna tıklanır.
2. Task.Run içinde 5 adımlı işlem başlar.
3. Her saniyede Dispatcher.Invoke ile TextBox güncellenir.
4. UI her adımda yanıt vermeye devam eder.

Not: Eğer Dispatcher.Invoke() yerine doğrudan txtDurum.Text = ... yazılsaydı, UI thread dışından erişim hatası oluşurdu. Alternatif olarak Dispatcher.BeginInvoke() kullanılabilir, bu versiyonda UI thread bloklanmaz.


TL;DR (Özet)

  • async metodu asenkron hale getirir, await işlemi bekler.
  • Task ve Task<T> asenkron işlemlerin sonucunu temsil eder.
  • await CPU'yu bloklamaz, işlem tamamlandığında devam eder.
  • Task.WhenAll() ile birden fazla görev aynı anda yürütülebilir.
  • Hatalar try-catch ile yönetilir; async void sadece event'lerde kullanılmalıdır.

İlişkili Makaleler

C# Process ve Thread Yönetimi

C#’ta process ve thread yönetimini öğrenin. Çok iş parçacıklı yapı, süreç kontrolü ve sistem kaynaklarıyla çalışma örneklerle.