27 Eylül 2012 Perşembe

C# ile BackgroundWorker

Merhaba değerli Webkolog.net takipçileri, bugünkü yazımızda C# .NET Framework 4 ile Windows Forms uygulamalarımızda uzun süren ve kullanıcı arayüzünü (UI) dondurabilecek işlemleri arka planda, ayrı bir iş parçacığında (thread) güvenli bir şekilde çalıştırmamızı sağlayan BackgroundWorker bileşenini yakından inceleyeceğiz. Uygulamanın tepkiselliğini korumak ve kullanıcı deneyimini kesintiye uğratmamak için BackgroundWorker vazgeçilmez bir araçtır. Gelin, BackgroundWorker'ın temel özelliklerini, olaylarını ve kullanım senaryolarını birlikte keşfedelim.

BackgroundWorker Bileşeni Nedir?

BackgroundWorker, .NET Framework'ün System.ComponentModel ad alanında bulunan ve Windows Forms uygulamalarında asenkron işlemleri yönetmek için özel olarak tasarlanmış bir bileşendir. Uzun süren hesaplamalar, dosya işlemleri, ağ istekleri gibi görevler doğrudan UI thread'i üzerinde çalıştırıldığında uygulamanın donmasına neden olur. BackgroundWorker, bu tür görevleri ayrı bir iş parçacığında çalıştırarak UI'ın tepkisel kalmasını sağlar ve görevin ilerlemesini UI'a güvenli bir şekilde raporlama, tamamlandığında bilgi verme veya iptal etme mekanizmaları sunar.

BackgroundWorker Kontrolünün Temel Özellikleri ve Olayları

BackgroundWorker, asenkron işlemleri yönetmek için aşağıdaki önemli özellik ve olaylara sahiptir:

  • WorkerReportsProgress: Eğer true olarak ayarlanırsa, ReportProgress metodu çağrıldığında ProgressChanged olayının tetiklenmesine izin verir.
  • WorkerSupportsCancellation: Eğer true olarak ayarlanırsa, CancelAsync metodu çağrıldığında asenkron görevin iptal edilmesine izin verir.
  • IsBusy: BackgroundWorker'ın şu anda bir işlem çalıştırıp çalıştırmadığını gösterir (true ise çalışıyor).
Temel Olaylar:
  • DoWork: Arka planda çalışacak olan kodun yazıldığı olaydır. Bu olay, UI thread'inden ayrı bir iş parçacığında tetiklenir. Burada UI kontrollerine doğrudan erişim yapılmamalıdır.
  • ProgressChanged: ReportProgress metodu çağrıldığında tetiklenir. Bu olay, UI thread'i üzerinde tetiklendiği için, arka plan görevinin ilerlemesini kullanıcı arayüzüne bildirmek için güvenli bir şekilde kullanılabilir.
  • RunWorkerCompleted: Arka plan görevi tamamlandığında (başarılı, hata veya iptal edildiğinde) tetiklenir. Bu olay da UI thread'i üzerinde tetiklendiği için, görevin sonucunu UI'a yansıtmak veya UI'ı eski haline getirmek için güvenli bir yerdir.
Temel Metotlar:
  • RunWorkerAsync(): Arka plan görevini başlatır. Bu metot çağrıldığında DoWork olayı tetiklenir.
  • RunWorkerAsync(object argument): Arka plan görevini başlatır ve DoWork olay işleyicisine parametre olarak bir argüman göndermenizi sağlar.
  • ReportProgress(int percentProgress): ProgressChanged olayını tetikler ve görevin yüzde kaçının tamamlandığını belirtir.
  • ReportProgress(int percentProgress, object userState): ProgressChanged olayını tetikler ve yüzdeye ek olarak herhangi bir nesne (userState) göndermenizi sağlar.
  • CancelAsync(): Eğer WorkerSupportsCancellation true ise, arka plan görevine iptal isteği gönderir. DoWork olayı içinde bu iptal isteğini kontrol etmek ve işi durdurmak geliştiricinin sorumluluğundadır.

BackgroundWorker Kullanım Senaryoları

1. Uzun Süren Bir İşlemi Arka Planda Çalıştırma ve İlerleme Gösterme

Bu örnekte, bir döngüyü arka planda çalıştıracak ve bir ProgressBar ile ilerlemesini göstereceğiz.

using System;
using System.ComponentModel; // BackgroundWorker için
using System.Windows.Forms;
using System.Threading; // Thread.Sleep için

public partial class Form1 : Form
{
    private BackgroundWorker backgroundWorker1; // Tasarımcıda eklenen BackgroundWorker bileşeni
    private ProgressBar progressBar1;         // Tasarımcıda eklenen ProgressBar
    private Button buttonBaslat;             // Tasarımcıda eklenen Başlat butonu
    private Button buttonIptal;              // Tasarımcıda eklenen İptal butonu
    private Label labelDurum;                // Tasarımcıda eklenen Durum Label'ı

    public Form1()
    {
        InitializeComponent();

        // Tasarımcıda eklenen kontrolleri varsayalım
        // backgroundWorker1, progressBar1, buttonBaslat, buttonIptal, labelDurum

        // BackgroundWorker özelliklerini ayarla
        this.backgroundWorker1.WorkerReportsProgress = true;  // İlerleme raporlamaya izin ver
        this.backgroundWorker1.WorkerSupportsCancellation = true; // İptal etmeye izin ver

        // Olay işleyicilerini bağla
        this.backgroundWorker1.DoWork += new DoWorkEventHandler(backgroundWorker1_DoWork);
        this.backgroundWorker1.ProgressChanged += new ProgressChangedEventHandler(backgroundWorker1_ProgressChanged);
        this.backgroundWorker1.RunWorkerCompleted += new RunWorkerCompletedEventHandler(backgroundWorker1_RunWorkerCompleted);

        // Buton olaylarını bağla
        this.buttonBaslat.Click += new EventHandler(buttonBaslat_Click);
        this.buttonIptal.Click += new EventHandler(buttonIptal_Click);

        // Başlangıç durumu
        this.progressBar1.Minimum = 0;
        this.progressBar1.Maximum = 100;
        this.progressBar1.Value = 0;
        this.labelDurum.Text = "Hazır";
        this.buttonIptal.Enabled = false;
    }

    private void buttonBaslat_Click(object sender, EventArgs e)
    {
        if (!this.backgroundWorker1.IsBusy) // Eğer bir işlem çalışmıyorsa
        {
            this.labelDurum.Text = "İşlem başlatılıyor...";
            this.progressBar1.Value = 0;
            this.buttonBaslat.Enabled = false;
            this.buttonIptal.Enabled = true;

            // Arka plan görevini başlat (argüman göndermiyoruz)
            this.backgroundWorker1.RunWorkerAsync(); 
        }
    }

    private void buttonIptal_Click(object sender, EventArgs e)
    {
        if (this.backgroundWorker1.WorkerSupportsCancellation == true)
        {
            // Arka plan görevine iptal isteği gönder
            this.backgroundWorker1.CancelAsync();
            this.labelDurum.Text = "İptal isteği gönderildi...";
        }
    }

    // Arka plan görevi burada çalışır
    private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
    {
        BackgroundWorker worker = sender as BackgroundWorker;

        for (int i = 1; i <= 100; i++)
        {
            // İptal isteği var mı kontrol et
            if (worker.CancellationPending == true)
            {
                e.Cancel = true; // Görevi iptal olarak işaretle
                break;
            }
            else
            {
                // Uzun süren bir işlemi simüle et
                Thread.Sleep(50); // 50 milisaniye bekle

                // İlerlemeyi UI'a bildir
                worker.ReportProgress(i); 
            }
        }
    }

    // İlerleme durumu değiştiğinde UI burada güncellenir (UI thread'i üzerinde çalışır)
    private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        this.progressBar1.Value = e.ProgressPercentage; // ProgressBar'ı güncelle
        this.labelDurum.Text = "İlerleme: %" + e.ProgressPercentage.ToString(); // Label'ı güncelle
    }

    // Arka plan görevi tamamlandığında (başarılı, hata veya iptal) UI burada güncellenir
    private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        if (e.Cancelled == true)
        {
            this.labelDurum.Text = "İşlem İptal Edildi!";
        }
        else if (e.Error != null)
        {
            this.labelDurum.Text = "Hata Oluştu: " + e.Error.Message;
            MessageBox.Show("İşlem sırasında bir hata oluştu: " + e.Error.Message, "Hata", MessageBoxButtons.OK, MessageBoxIcon.Error);
        }
        else
        {
            this.labelDurum.Text = "İşlem Tamamlandı!";
        }

        this.progressBar1.Value = 100; // İşlem bitince %100 yap
        this.buttonBaslat.Enabled = true;
        this.buttonIptal.Enabled = false;
    }
}
2. Argüman Gönderme ve Sonuç Alma

RunWorkerAsync() ile argüman gönderebilir ve DoWork içinde bu argümanı kullanabiliriz. İşlem sonunda bir sonuç döndürmek de mümkündür.

using System;
using System.ComponentModel;
using System.Windows.Forms;
using System.Threading;

public partial class Form1 : Form
{
    private BackgroundWorker backgroundWorker2;
    private Button buttonHesapla;
    private Label labelSonuc;

    public Form1()
    {
        InitializeComponent();
        backgroundWorker2 = new BackgroundWorker(); // Yeni bir instance
        backgroundWorker2.WorkerReportsProgress = false;
        backgroundWorker2.WorkerSupportsCancellation = false;

        backgroundWorker2.DoWork += new DoWorkEventHandler(backgroundWorker2_DoWork);
        backgroundWorker2.RunWorkerCompleted += new RunWorkerCompletedEventHandler(backgroundWorker2_RunWorkerCompleted);

        this.buttonHesapla.Click += new EventHandler(buttonHesapla_Click);
        this.labelSonuc.Text = "Hazır";
    }

    private void buttonHesapla_Click(object sender, EventArgs e)
    {
        if (!backgroundWorker2.IsBusy)
        {
            this.labelSonuc.Text = "Hesaplanıyor...";
            this.buttonHesapla.Enabled = false;
            // Argüman olarak bir sayı gönderelim
            backgroundWorker2.RunWorkerAsync(10); // Argümanı DoWork'e gönder
        }
    }

    private void backgroundWorker2_DoWork(object sender, DoWorkEventArgs e)
    {
        // Argümanı al
        int sayi = (int)e.Argument; 
        long faktoriyel = 1;

        for (int i = 1; i <= sayi; i++)
        {
            faktoriyel *= i;
            Thread.Sleep(200); // Hesaplamayı simüle et
        }

        // Sonucu RunWorkerCompleted olayına göndermek için Result özelliğini kullan
        e.Result = faktoriyel; 
    }

    private void backgroundWorker2_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        if (e.Error != null)
        {
            this.labelSonuc.Text = "Hata: " + e.Error.Message;
        }
        else if (e.Cancelled)
        {
            this.labelSonuc.Text = "Hesaplama iptal edildi.";
        }
        else
        {
            // Sonucu al ve UI'a yansıt
            this.labelSonuc.Text = "Faktöriyel Sonucu: " + e.Result.ToString();
        }
        this.buttonHesapla.Enabled = true;
    }
}

Dikkat Edilmesi Gerekenler

  • UI Güvenliği: DoWork olayı içinde kesinlikle UI kontrollerine doğrudan erişmeyin veya onları güncellemeye çalışmayın. Bu, "cross-thread operation not valid" hatasına yol açar. UI güncellemeleri için ReportProgress (ve dolayısıyla ProgressChanged olayı) veya RunWorkerCompleted olaylarını kullanmalısınız, çünkü bu olaylar UI thread'i üzerinde tetiklenir.
  • Hata Yönetimi: DoWork içinde oluşan herhangi bir hata, RunWorkerCompleted olayındaki e.Error özelliğine atanır. Hataları burada kontrol etmeli ve kullanıcıya bildirmelisiniz.
  • İptal Mekanizması: BackgroundWorker, işlemi otomatik olarak iptal etmez. CancelAsync() sadece bir sinyal gönderir. DoWork olayında, belirli aralıklarla worker.CancellationPending özelliğini kontrol etmeli ve true ise işlemi durdurup e.Cancel = true; ayarını yapmalısınız.
  • Performans: ReportProgress metodunu çok sık çağırmak, UI thread'ini gereksiz yere meşgul edebilir. İlerleme raporlamasını makul aralıklarla yapın.
---

Evet sevgili Webkolog.net okurları, bu yazımızda C# .NET Framework 4 ile BackgroundWorker bileşeninin temel özelliklerini, olaylarını ve kullanım senaryolarını detaylı bir şekilde inceledik. BackgroundWorker, uzun süren işlemleri arka planda çalıştırarak Windows Forms uygulamalarınızın tepkiselliğini korumanın ve kullanıcı deneyimini artırmanın en etkili yollarından biridir. Karmaşık hesaplamalar, dosya indirmeler veya ağ işlemleri gibi durumlarda uygulamanızın donmasını engellemek için bu bileşenden faydalanabilirsiniz. Umarım bu rehber, C# .NET Framework 4 ile uygulamalar geliştirirken size yardımcı olur. Bir sonraki yazımda, C# dilinin diğer önemli yapı taşlarını keşfetmeye devam edeceğiz. Webkolog.net'i takipte kalın!

Hepinize sorunsuz ve akıcı uygulamalar geliştirme süreçleri dilerim!

0 yorum:

Yorum Gönder