CodeGym /Kurslar /C# SELF /Ənənəvi Thread-lərd...

Ənənəvi Thread-lərdə istisnalar

C# SELF
Səviyyə , Dərs
Mövcuddur

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();

Handler işlənməmiş thread istisnaları üçün çağırılır, amma .NET Framework-də thread-i "diriltməyə" və ya prosesi dayanmadan saxlamağa imkan vermir. .NET (Core/5+)-də bu handler xətanı loglayır; tətbiq qalan thread-lər işləkdirsə davam edə bilər.

İstisna işlənməsində fərqlər — Thread vs Task

Thread
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".

Şərhlər
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION