- Konu sahibi
- #1
İşlem sırasında WinForms arayüzü donuyorsa uzun süren kod büyük olasılıkla kullanıcı arabirimi iş parçacığında çalışıyordur. Çözüm, dosya ağı veya veritabanı işlemlerinde doğrudan asenkron API kullanmak; yoğun hesaplamalarda ise işi
WinForms denetimleri, onları oluşturan UI iş parçacığından yönetilir. Bu iş parçacığında uzun süren bir döngü, büyük dosya işlemi, veritabanı sorgusu, ağ isteği veya hesaplama çalışırken Windows mesajları işlenemez; pencere yeniden çizilemez ve uygulama donmuş gibi görünür.
İlk olarak işlemin türünü belirleyin:
Aşağıdaki örnek varsayımsaldır.
Burada düğme önce devre dışı bırakılır, hesaplama arka plan iş parçacığında yapılır ve
İşlem sürerken kullanıcı aynı düğmeye tekrar basabiliyorsa aynı iş birden fazla kez başlayabilir. Düğmeyi devre dışı bırakmak bunu önler. Daha gelişmiş senaryolarda
Kullandığınız kütüphane
Asenkron metot olay işleyicisinden çağrılmalı ve olay işleyicisi
.NET 9 ve sonraki WinForms sürümlerinde
Ancak çoğu durumda ilerleme bilgisini
İşlem yine donuyorsa Visual Studio'da duraklatılmış uygulamanın çağrı yığınını inceleyin. UI iş parçacığında uzun bir metot,
Test sırasında işlem başlar başlamaz formu sürükleyin, yeniden boyutlandırın ve mümkünse başka bir denetimi kullanın. Pencere yanıt veriyor, işlem tamamlandığında sonuç güncelleniyor ve hata oluştuğunda düğme yeniden etkinleşiyorsa temel akış doğru kurulmuştur.
Form hâlâ yanıt vermiyorsa yalnızca dış metoda değil,
Task.Run ile arka plan iş parçacığına taşımaktır. İşlem boyunca denetimlere yalnızca UI iş parçacığından erişin, düğmeyi geçici olarak devre dışı bırakın ve hata ile iptal durumlarını try/catch/finally içinde yönetin. Böylece form yanıt vermeye devam eder ve işlem bittiğinde sonucu güvenli biçimde gösterebilirsiniz.Önce donmanın nedenini ayırın
WinForms denetimleri, onları oluşturan UI iş parçacığından yönetilir. Bu iş parçacığında uzun süren bir döngü, büyük dosya işlemi, veritabanı sorgusu, ağ isteği veya hesaplama çalışırken Windows mesajları işlenemez; pencere yeniden çizilemez ve uygulama donmuş gibi görünür.
İlk olarak işlemin türünü belirleyin:
| İşlem türü | Uygulanacak yaklaşım | Önemli sınır |
|---|---|---|
| Dosya, ağ veya veritabanı işlemi | Kütüphanenin async/await API'sini doğrudan kullanın | Gereksiz yere Task.Run eklemek çözüm değildir |
| Yoğun CPU hesaplaması | Hesaplamayı Task.Run içine taşıyın | Hesaplama sırasında kontrollere erişmeyin |
| Eski, yalnızca eşzamanlı API | Arka plan görevi veya uygun bir asenkron API kullanın | UI güncellemelerini tekrar UI iş parçacığına taşıyın |
await tek başına her kodu arka planda çalıştırmaz. Örneğin uzun bir for döngüsünü yalnızca async anahtar sözcüğü ekleyerek yazmak UI iş parçacığını boşaltmaz. Beklenebilir bir asenkron işlem yoksa kod yine formun bulunduğu iş parçacığında çalışır.CPU yoğun işlemi güvenli biçimde taşıma
Aşağıdaki örnek varsayımsaldır.
Calculate yalnızca hesaplama yapar ve hiçbir forma, düğmeye veya metin kutusuna erişmez. button1 ve labelStatus, tasarım ekranında forma eklenmiş denetimlerdir.
C#:
private async void button1_Click(object sender, EventArgs e)
{
button1.Enabled = false;
labelStatus.Text = "Hesaplanıyor...";
try
{
int input = 100_000;
int result = await Task.Run(() => Calculate(input));
labelStatus.Text = $"Sonuç: {result}";
}
catch (OperationCanceledException)
{
labelStatus.Text = "İşlem iptal edildi.";
}
catch (Exception ex)
{
labelStatus.Text = "İşlem başarısız.";
MessageBox.Show(this, ex.Message, "Hata");
}
finally
{
button1.Enabled = true;
}
}
private static int Calculate(int input)
{
int total = 0;
for (int i = 0; i < input; i++)
total += i % 7;
return total;
}
Burada düğme önce devre dışı bırakılır, hesaplama arka plan iş parçacığında yapılır ve
await sonrasında sonuç tekrar UI iş parçacığına dönen olay işleyicisinde gösterilir. İşlem tamamlandığında etiket sonucu gösteriyorsa hesaplama akışı bitmiştir; ancak bu, sonucun iş açısından doğru olduğunu kanıtlamaz. Hesaplama girdilerini ve beklenen aralığı ayrıca doğrulamanız gerekir.Arka plandaki kodda denetim kullanmayın
Task.Run içindeki koddan doğrudan textBox1.Text, progressBar1.Value veya başka bir denetim okumak ve yazmak güvenli değildir. Gerekli girdileri Task.Run çağrısından önce yerel değişkenlere alın, sonucu geri döndürün ve denetimleri await sonrasında güncelleyin.İşlem sürerken kullanıcı aynı düğmeye tekrar basabiliyorsa aynı iş birden fazla kez başlayabilir. Düğmeyi devre dışı bırakmak bunu önler. Daha gelişmiş senaryolarda
CancellationToken kullanarak iptal düğmesi ekleyebilir, döngü içinde token.ThrowIfCancellationRequested() çağırabilirsiniz.Dosya, ağ ve veritabanı işlemlerinde doğru yöntem
Kullandığınız kütüphane
ReadAsync, WriteAsync, ExecuteReaderAsync veya benzeri bir API sağlıyorsa onu kullanın. Bu işlemler için Task.Run ile bir eşzamanlı çağrıyı sarmalamak yerine doğrudan asenkron metodu await edin. Böylece bekleme sırasında UI iş parçacığı başka Windows mesajlarını işleyebilir.Asenkron metot olay işleyicisinden çağrılmalı ve olay işleyicisi
async void olabilir; diğer yardımcı metotlar mümkün olduğunca async Task veya async Task<T> döndürmelidir. Task.Wait(), .Result ve uzun işlem sırasında Thread.Sleep kullanmak UI iş parçacığını yeniden bloke edebilir. Özellikle bir asenkron işlemin sonucunu .Result ile beklemek kilitlenmeye yol açabilir.Arka plandan forma ilerleme yazmanız gerekiyorsa
.NET 9 ve sonraki WinForms sürümlerinde
Control.InvokeAsync, arka plan kodundan UI iş parçacığına asenkron ve engellemesiz güncelleme göndermek için kullanılabilir..NET Framework'te bu API bulunmaz; eski projelerde Invoke, BeginInvoke veya BackgroundWorker gibi sürümle uyumlu yöntemler gerekir. BackgroundWorker hâlâ desteklense de yeni asenkron kod için genel tercih async/await yaklaşımıdır.Ancak çoğu durumda ilerleme bilgisini
Task.Run içindeki döngüden doğrudan forma yazmak yerine IProgress<T> kullanmak daha düzenlidir. İlerleme güncellemesi çok sık gönderilirse UI iş parçacığı yine meşgul olabilir; her küçük adımda değil, anlamlı aralıklarla bildirim göndermek gerekir.İşlem yine donuyorsa Visual Studio'da duraklatılmış uygulamanın çağrı yığınını inceleyin. UI iş parçacığında uzun bir metot,
.Wait() veya .Result görüyorsanız beklemeyi await ile değiştirin. UI iş parçacığı boşta görünmesine rağmen sonuç gelmiyorsa sorun donma değil; ağ, dosya, veritabanı veya kilitlenme beklemesi olabilir. Bu durumda ilgili işlemin zaman aşımı, hata ve iptal davranışını ayrı ayrı kontrol edin.Çözümün doğru çalıştığını nasıl anlarsınız?
Test sırasında işlem başlar başlamaz formu sürükleyin, yeniden boyutlandırın ve mümkünse başka bir denetimi kullanın. Pencere yanıt veriyor, işlem tamamlandığında sonuç güncelleniyor ve hata oluştuğunda düğme yeniden etkinleşiyorsa temel akış doğru kurulmuştur.
Form hâlâ yanıt vermiyorsa yalnızca dış metoda değil,
await öncesindeki tüm kodlara bakın. Büyük veri dönüştürme, döngü, seri hale getirme veya senkron dosya erişimi await satırından önce çalışıyorsa donmanın nedeni orada olabilir. İşlem tamamlanıyor ama arayüz güncellenmiyorsa arka plan kodunun denetime doğrudan erişmediğini ve güncellemenin UI iş parçacığında yapıldığını kontrol edin. Invoke ile kilitlenme yaşıyorsanız, hedef çerçeveniz.NET 9 veya üzeriyse InvokeAsync yaklaşımına geçin; değilse karşılıklı bekleyen kilitleri kaldırın ve UI iş parçacığını bekleyen senkron çağrıları kullanmayın.