1. Giriş
Task sinfi və ya asinxron metodlarla (async/await) işləyərkən istisnaları adi qaydada tuta bilərik — await ətrafında try-catch ilə və ya ContinueWith vasitəsilə. İstisnalar "itmir", çağıran axına geri qayıdır.
Amma əgər biz Thread vasitəsilə bir thread yaradırıqsa, vəziyyət daha mürəkkəbləşir. Hər thread-in öz giriş nöqtəsi var (ThreadStart) və öz icra konteksti. Əgər thread daxilində işlənməmiş istisna baş verərsə, o əsas axına "qayıtmır" — yalnız o thread-də atılır.
- .NET Framework-da: bir thread-də işlənməmiş istisna bütün tətbiqi dayandıra bilər.
- .NET (Core/5+)-də: yalnız həmin thread bitir, tətbiq işləməyə davam edir (bu gizli səhvlərə gətirə bilər).
Nəticə: əgər thread daxilində istisnaları tutmasınız, böyük ehtimalla onları sadəcə görməyəcəksiniz. Buna görə də thread-lərdə düzgün xəta işlənməsi vacibdir.
Maraqlı fakt: Thread-dən çıxan istisnalar nəzarətsiz ninja kimidir: yox olurlar və sonra siz başa düşməyə çalışırsınız ki, niyə məntiq işləmədi.
2. Thread daxilində istisnalar necə işləyir?
using System;
using System.Threading;
class Program
{
static void Main()
{
Thread thread = new Thread(DoWork);
thread.Start();
// Bitməsini gözləyək ki, nə olacağını görək
thread.Join();
Console.WriteLine("Main normal şəkildə başa çatdı");
}
static void DoWork()
{
Console.WriteLine("Müstəqil thread-də işləyirik...");
throw new Exception("Bələd: Thread-də səhv baş verdi.");
}
}
Platformadan (köhnə .NET Framework və ya müasir .NET Core/5/6/7/8/9) asılı olaraq davranış fərqli olacaq: ya bütün tətbiq çökmək, ya da yalnız həmin thread. Amma əsas məqam — istisna əsas thread-ə çatmayacaq və onu xaricdən işləyə bilməyəcəksiniz.
Vacib! thread.Join()-i try-catch-lə bürümək başqa thread-dən gələn istisnanı tutmağa kömək etmir — istisna həmin thread daxilində "yaşayıb" ölür.
3. Thread-də istisnaları necə tutmaq lazımdır?
Yalnız thread daxilində — siz Thread-in konstruktoruna ötürdüyünüz funksiyada. Hər nə istisna atacaqsa, onu try-catch-lə bürün.
static void DoWork()
{
try
{
Console.WriteLine("İşləyirik...");
throw new Exception("Yenə bir şey səhv getdi!");
}
catch (Exception ex)
{
Console.WriteLine($"[Thread] İstisna tutuldu: {ex.Message}");
// Burada loglama, UI/serveryə göndərmə və s. etmək olar.
}
}
Thread-lərdə istisna işlənməsi — thread kodunun məsuliyyətidir. Çağıran kodun xətanı avtomatik tutacağını gözləmək olmaz.
4. Əsas thread-də necə öyrənmək olar ki, başqa thread-də nəsə səhv getdi?
Real tətbiqlərdə səhv haqqında məlumatı əsas thread-ə çatdırmaq vacibdir.
- Thread-safe mexanizmlərdən istifadə edin, məs.: ConcurrentQueue<Exception>, istisnaları thread-dən ötürmək üçün.
- İşçi thread-dən event/delegate qaldırın.
- Task-ı üstün tutun — o, istisnanı await-ə "qutudan çıxmış" şəkildə çatdırır.
Nümunə: xətanı xüsusi yerə yığmaq
using System;
using System.Threading;
class Program
{
static Exception? threadException = null;
static void Main()
{
Thread thread = new Thread(DoWork);
thread.Start();
thread.Join();
if (threadException != null)
{
Console.WriteLine($"Başqa thread-də səhv baş verdi: {threadException.Message}");
}
else
{
Console.WriteLine("Thread səhvsiz başa çatdı.");
}
}
static void DoWork()
{
try
{
throw new Exception("Başqa thread-də bələd!");
}
catch (Exception ex)
{
threadException = ex;
}
}
}
Qeyd: bu yanaşma sinxron gözləmə (Join()) üçün uyğundur. Əgər thread "öz həyatını yaşayır" və ya xətalar çoxdursa — ConcurrentQueue<Exception>, eventlər və ya digər kommunikasiya mexanizmlərindən istifadə edin.
5. Task ilə müqayisə: niyə xətaların işlənməsi daha asandır
async Task FooAsync()
{
throw new Exception("Task-də xəta!");
}
try
{
await FooAsync();
}
catch (Exception ex)
{
Console.WriteLine($"Xətanı tutduq: {ex.Message}");
}
Burada hər şey açıqdır: səhv onu await-lə gözlədiyiniz yerə "çatır". Klassik Thread-də xəta thread daxilində qalır və xüsusi tədbirlər olmadan yuxarı ötürülmür. Bu Task və müasir abstraksiyalara üstünlük verməyin əsas arqumentlərindən biridir.
6. Praktik nümunə
UI-tətbiqlərdə (WPF/WinForms) thread-lər interfeysi bloklamamaq üçün istifadə olunur. İdarə olunmayan istisnalar "boz ekran"a və qəribə donmalara gətirə bilər.
Pis nümunə (thread-də xəta işlənmir)
Thread thread = new Thread(() =>
{
// Uzun müddət düşünürük
Thread.Sleep(5000);
throw new Exception("Hər şey itdi!"); // heç kim tutmayacaq
});
thread.Start();
Yaxşı nümunə (xətanı tutub istifadəçiyə xəbər veririk)
Thread thread = new Thread(() =>
{
try
{
Thread.Sleep(5000);
throw new Exception("Nəsə səhvdir");
}
catch (Exception ex)
{
// MessageBox göstərmək, loglamaq və ya UI-ə ötürmək olar
Console.WriteLine($"Thread-də xəta: {ex.Message}");
}
});
thread.Start();
7. Faydalı nüanslar
Thread üçün işlənməmiş istisnalar üzrə qlobal hook
AppDomain.CurrentDomain.UnhandledException += (sender, args) =>
{
Console.WriteLine($"Qlobal tutduq: {((Exception)args.ExceptionObject).Message}");
};
Thread thread = new Thread(() =>
{
throw new Exception("Exterminatus!");
});
thread.Start();
İstisna işlənməsində fərqlər — Thread vs Task
|
|
|
|---|---|---|
| Harada tutmalı | Thread daxilində | Çağıran kodda (await, ContinueWith və s.) |
| Nəticələr | İstisna itir/_thread-i öldürür (və ya .NET Framework-də bütün tətbiqi) | İstisna gözləmə yerinə (await) çatır |
| Yuxarı bildiriş | Yalnız əllə (dəyişənlər, eventlər, queue-lar) | await vasitəsilə, sinxron gözləmədə AggregateException |
| Loglama | Thread kodunda əl ilə etmək lazımdır | Adətən try-catch-də await ətrafında |
| Kontekst | Valideyn thread-dən müstəqildir | Task çağıranın synchronization context-ini istifadə edir (məsələn, WPF-də UI konteksti) |
8. Thread-lə iş zamanı tipik səhvlər
Səhv №1: thread daxilində istisnaları tutmurlar.
Nəticədə tətbiqin bir hissəsi və ya bəzən bütün proses gözlənilmədən bitə və diaqnostika olmaya bilər.
Səhv №2: istisnanı əsas thread-də tutmağa çalışırlar.
Bu işləmir: try-catch ilə thread.Join() və ya thread.Start()-in ətrafını bürümək thread daxilində atılan istisnanı tutmayacaq.
Səhv №3: xəta haqqında məlumat itirirlər.
Əgər thread çöksə və siz istisnayı aydın şəkildə ötürməsəniz (dəyişən, queue, event), nə səbəbini nə də detalları öyrənə bilməzsiniz. Bu "ruhani" bug-lara gətirir.
Səhv №4: loglamanın olmaması.
Həmişə thread-də xətaları loglayın, hətta görsənmirsə ki, "heç nə ciddi deyil".
GO TO FULL VERSION