CodeGym /Kurslar /C# SELF /Thread-in həyat dövrü və onun idarə olunması

Thread-in həyat dövrü və onun idarə olunması

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

1. Giriş

Təsəvvür edin ki, thread — bu yorulmayan işçi kimidir, ona bir iş tapşırırsınız. İşçi yatmış ola bilər (hələ işi başlamayıb), tər tökərək işləyir (sizin metod icra olunur), yeni tapşırıq üçün gözləyir (idle), yaxud işi bitirib (tamamlandı).

C# (ümumiyyətlə .NET)də thread-in həyat dövrü bir neçə vəziyyətdən ibarətdir:

  • Unstarted — thread yaradılıb, amma hələ işə salınmayıb.
  • Running — thread icra olunur.
  • WaitSleepJoin — thread müvəqqəti işləməyir (məsələn, siqnal gözləyir və ya "yuxudadır").
  • Stopped — thread tapşırığı yerinə yetirib və bitib.

Bu dövrü belə sxemlə təsəvvür etmək olar:

stateDiagram-v2
    [*] --> Unstarted
    Unstarted --> Running: Start()
    Running --> WaitSleepJoin: Wait/Sleep/Join
    WaitSleepJoin --> Running: Siqnal alındı/Vaxt bitdi
    Running --> Stopped: Metod bitdi
    WaitSleepJoin --> Stopped: Metod bitdi
    Stopped --> [*]

Hər şey Thread obyekti yaradılmasından başlayır, amma siz Start() çağırana qədər thread Unstarted vəziyyətində "yuxulayır". Start()-dan sonra thread Running-ə keçir. Əgər thread daxilində Thread.Sleep çağırılırsa və ya nəyisə gözləyirsə (məsələn, Monitor.Wait), o zaman o, gözləmə vəziyyətinə düşür. Metod tamamlandıqda isə thread ölür və artıq "canlanmır". Bu bir istiqamətli biletdir.

2. Praktika: Sadə thread-in həyat dövrü

Gəlin klassik nümunəyə baxaq:

using System;
using System.Threading;

class Program
{
    static void Main()
    {
        // Pozdəyən thread — hələ yalnız işi planlaşdırırıq
        Thread worker = new Thread(DoWork);

        Console.WriteLine($"Thread yaradıldıqdan sonra status: {worker.ThreadState}");

        // Thread-i işə salırıq
        worker.Start();
        Console.WriteLine($"Start()-dan sonra thread statusu: {worker.ThreadState}");

        // Əsas thread-i bir qədər yatırırıq ki, işçi thread işləsin
        Thread.Sleep(100);

        Console.WriteLine($"Bir az sonra thread statusu: {worker.ThreadState}");

        // worker bitənə qədər gözləyirik (join edirik)
        worker.Join();

        Console.WriteLine($"Bitirdikdən sonra thread statusu: {worker.ThreadState}");
        Console.WriteLine("Əsas thread bitdi");
    }

    static void DoWork()
    {
        Console.WriteLine("İşçi thread işə başladı!");
        Thread.Sleep(500);
        Console.WriteLine("İşçi thread işini bitirdi!");
    }
}

Proqram nə çap edəcək?

  1. Thread yaradıldıqdan sonra — status adətən Unstarted olacaq.
  2. Start()-dan sonra — adətən dərhal Running (amma bəzən Running | Background də görünə bilər).
  3. İcranın zamanı — status Running və ya WaitSleepJoin ola bilər, əgər thread "yatıb".
  4. Metod bitdikdən sonra — status Stopped olur.

Bu kod thread-in hansı vəziyyətdə ola biləcəyini başa düşmək üçün əla vasitədir. Müddətləri dəyişdirib statusun necə dəyişdiyini izləyə bilərsiniz.

3. Thread-in idarə olunması: əsas metodlar

Start: Start()

Bu aydındır, amma təkrarlayaq: thread yaratdıqdan sonra onu Start() ilə işə salın. Və bunu yalnız bir dəfə etmək olar: Start()-ı təkrar çağırmaq ThreadStateException-a səbəb olacaq.

Thread t = new Thread(MyMethod);
t.Start();   // OK
t.Start();   // Səhv!

Bitməni gözləmək: Join()

Bəzən lazım olur ki, thread tam bitənə qədər gözləyəsiniz. Bunun üçün Join() var.

Thread t = new Thread(MyMethod);
t.Start();
t.Join(); // Cari thread-i t bitənə qədər bloklayır

Əgər bir neçə threadiniz varsa, hər biri üçün Join() çağırmaq olar — əsas thread bütün "işçilərin" bitməsini gözləyəcək.

Variantlar: Join(int millisecondsTimeout) overload-u var, bu yalnız göstərilən vaxta qədər gözləyir, sonra davam edir.

// Ən çox 2 saniyə gözləyirik
if (t.Join(2000))
    Console.WriteLine("Thread vaxtında bitdi");
else
    Console.WriteLine("Gözləməkdən usandıq...");

Məcburi dayandırma: niyə pis fikirdir

Köhnə .NET versiyalarında Thread.Abort() kimi metodlar vardı ki, thread-i "öldürməyə" imkan verirdi. İndi onlar demək olar ki, istifadə olunmur — təhlükəlidir və proqramı qəribə vəziyyətdə qoymaq olar. .NET fəlsəfəsi budur: thread könüllü şəkildə bitməlidir. Siz işçini "öldürmürsünüz" — ona nəzakətlə bildirirsiniz ki, iş günü bitdi.

4. Thread-i düzgün "dayandırmaq"

Thread-i dayandırmaq üçün ən doğru və təhlükəsiz metod — cancel flag və ya bitmə göstəricisi istifadə etməkdir, thread onu mütəmadi yoxlayır.

class Worker
{
    private volatile bool shouldStop = false;

    public void DoWork()
    {
        while (!shouldStop)
        {
            Console.WriteLine("İşləyirəm!");
            Thread.Sleep(300);
        }

        Console.WriteLine("Thread komandaya görə işini bitirir.");
    }

    public void RequestStop()
    {
        shouldStop = true;
    }
}

İstifadə:

Worker w = new Worker();
Thread t = new Thread(w.DoWork);
t.Start();

// Bir az gözləyirik
Thread.Sleep(1000);

// Thread-dən bitməsini xahiş edirik
w.RequestStop();
t.Join(); // Thread-in bitməsini gözləyirik

Vacib məqam: volatile

volatile axtarışı kompilyatora və prosessora deyir: "Bu sahəni keşləmə, həmişə aktual dəyəri götür!" Bu, thread-in stop flag-ı vaxtında görməsi üçün vacibdir. Əks halda (və ya digər sinxronizasiya vasitələri istifadə olunmayanda) thread sizin dəyişiklikləri görməyə bilər.

5. Thread-lərin gözləmə və yuxu vəziyyətinə keçməsi

Bəzən thread müvəqqəti işləməyir — ya yatır, ya gözləyir.

Yuxu: Thread.Sleep

Thread-ə istirahət vermək və ya icranı yavaşıtmaq istəyəndə Thread.Sleep(milliseconds) istifadə olunur.

// Thread 2 saniyə yatır
Thread.Sleep(2000);

Yuxu zamanı thread heç bir iş görmür.

Gözləmə / Join

Əsas thread uşaq thread-in bitməsini gözləyəndə (Join) əsas thread "pauza" vəziyyətində olur. Eyni şəkildə, əgər thread resursun açılmasını gözləyirsə (məsələn, monitor və ya digər sinxronizasiya primitivləri vasitəsilə), o da xüsusi gözləmə vəziyyətinə keçir.

6. Thread-in background/foreground idarəsi

.NET-də thread-lər iki növdür: foregroundbackground. Fərq sadədir:

  • Əgər prosesdə yalnız background thread-lər qalıbsa, proses avtomatik olaraq bitir.
  • Foregound thread-lər (əsas thread daxil olmaqla) bitmədən proses dayanmır.

Thread-i açıq şəkildə background etmək olar:

Thread t = new Thread(SomeMethod);
t.IsBackground = true; // Background kimi təyin etdik
t.Start();

Praktik nümunə — Demon vs adi thread

Thread t = new Thread(() =>
{
    while (true)
    {
        Console.WriteLine("Mən phantomam (background), məni dayandırmaq olmur!");
        Thread.Sleep(500);
    }
});
t.IsBackground = true; // Background etdik
t.Start();

Thread.Sleep(1200);
Console.WriteLine("Əsas thread işini bitirir");
// Main bitdikdə — proses ölür və bizim sonsuz thread də yox olur

Main bitdikdən sonra proses tamamlanır; background thread-lər avtomatik dayandırılır.

7. Faydalı nüanslar

Thread-lərlə nə etmək olmaz

  • Thread-i "yenidən işə salmaq" olmaz. Thread obyekti bir dəfə yaşayır: metod bitdikdən sonra thread ölər və Start()-ın yenidən çağırılması səhvə səbəb olacaq.
  • Başqasının thread-in məcburi dayandırılması olmazThread.Abort()Thread.Suspend() kimi üsullar köhnədir və təhlükəlidir.
  • Thread-in bitməsini nəzərə almamaq olmaz. Əgər thread fayllarla və ya digər resurslarla işləyirsə, onları düzgün şəkildə buraxın.

Vəziyyəti yoxlamaq və həyat dövrünü idarə etmək

if (t.IsAlive)
{
    Console.WriteLine("Thread hələ də yaşayır");
}
else
{
    Console.WriteLine("Thread bitdi");
}

IsAlivetrue olur, thread öz metodunu icra edərkən; bitdikdən sonra — false.

.NET-də sadə thread-in həyat dövrü

Vəziyyət Necə keçmək olar Bu nə deməkdir? Necə çıxmaq olar
Unstarted
new Thread(...)
Thread yaradılıb, işə salınmayıb Start() çağırmaq
Running
Start()
Thread işini icra edir Metod bitir
WaitSleepJoin Sleep(), Join(), gözləmə Thread müvəqqəti aktiv deyil Gözləmə bitir
Stopped Thread-in metodu bitdi Thread "öldü" Qayıtmır — son

FAQ: life cycle idarəetməsi

Sual: Thread-i əmrlə öldürmək olarmı?
Cavab: Xeyr və lazım deyil; thread-lər öz bitməsinə nəzarət etməlidir. Cancel flag istifadə edin.

Sual: Eyni Thread obyekti yenidən istifadə oluna bilərmi?
Cavab: Xeyr. Yeni iş üçün yeni obyekt yaradın.

Sual: Əsas thread bitdi, amma child thread işləyirsə nə olacaq?
Cavab: Əgər child thread background-dursa (IsBackground == true), tətbiq bitəcək. Əks halda proses child thread-lər bitənə qədər yaşayacaq.

Sual: Thread ləğv olunduqda resurslar necə təmizlənməlidir?
Cavab: Thread metodunda try...finally bloklarından istifadə edin ki, resurslar hər halda buraxılsın.

8. Thread-lərlə işləyərkən tipik səhvlər və onlardan necə qaçmaq

Səhv №1: eyni Thread obyektinin təkrar istifadəsi.
Eyni thread obyektini bir neçə dəfə işə salmaq olmaz. Bitdikdən sonra onu yenidən Start etmək exception-a gətirib çıxaracaq.

Səhv №2: thread-də xarici resursların düzgün buraxılmaması.
Əgər thread fayllarla, şəbəkə ilə və ya digər resurslarla işləyirsə, mütləq bunları düzgün bağlayın. finally blokları və ya using konstruktorları istifadə etmək tövsiyə olunur ki, sızıntılar və bloklanmalar olmasın.

Səhv №3: çox sayda thread yaratmaq.
Çox thread yaratmaq debugging-i çətinləşdirir və performansı aşağı sala bilər. Hər yeni thread əlavə overhead gətirir — ona görə də lazım olmayan thread-ləri yaratmayın.

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