CodeGym /Kurslar /C# SELF /Thread sinfi ilə axınları işə salmaq

Thread sinfi ilə axınları işə salmaq

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

1. Giriş

Əgər prosesi supermarket kimi təsəvvür etsək, axınlar (thread) — müxtəlif kassalarda işləyən kassirlərdir, eyni vaxtda müştərilərə xidmət edir. Bütün kassirlər eyni mağazada işləyir, amma hər biri öz işi ilə paralel məşğul olur, nəticədə iş daha sürətli və effektiv gedir. Proses içindəki axınlar bir neçə işi eyni zamanda yerinə yetirməyə imkan verir, ümumi resursları bölüşdürür və bir tətbiq daxilində işi əlaqələndirir. Əsas üstünlük — axınlar əgər prosessor çoxtapşırıqlığı dəstəkləyirsə, həqiqətən paralel işləyə bilər.

Axınların praktik zərurəti

Niyə bir neçə axın işə salmalıyıq? Real həyatdan bir neçə situasiya:

  • GUI ilə tətbiq yazırsınız və uzun əməliyyat zamanı proqramın "donmasını" istəmirsiniz.
  • Bir neçə faylı eyni zamanda yükləməlisiniz.
  • Oyunda düşmənlərin müstəqil şəkildə "öz ağlı" ilə işləməsi lazımdır.

Təəccüblüdür ki, bir çox proqramlarda hələ də təcrübəsiz axın istifadəsi səbəbindən "donmalar" olur. Bu gün biz belə anlaşılmazlıqlardan necə qaçmağı öyrənəcəyik.

Thread sinfi: əl ilə çoxaxınlılığın əsasları

Thread sinfi — .NET-də çoxaxınlı proqramlaşdırmanın "dinozavr"ıdır. Daha müasir alətlər (Task, async/await) ortaya çıxsa da, Thread ilə işləmək hələ də mənalıdır, xüsusən də axınlara "sıfırdan" nəzarət etmək istəyirsinizsə.

Axının yaradılma sxemi

  1. Thread sinfinin obyektini yaradın, icra olunacaq metodu ona ötürün.
  2. Start() metodu ilə axını işə salın.
  3. (İxtiyari) Nə baş verdiyini yoxlayın — hər şey paralel işləyə bilər!

2. Thread ilə axının işə salınması

Gəlin paralelliyin sehrini praktikada hiss edək. Şərti tətbiqimizə müəyyənə qədər sayan və gedişatı çap edən kiçik bir sinif əlavə edək. Bu işi ayrıca axında necə işə salmağı öyrənəcəyik.

using System;
using System.Threading;

class Program
{
    static void Main()
    {
        Console.WriteLine("Əsas axın başladı!");

        // İcra üçün metodu göstərərək axın obyekti yaradırıq
        Thread workerThread = new Thread(CountToTen);

        // Yeni axını işə salırıq
        workerThread.Start();

        // Əsas axın da öz işini görür: nöqtələr yazır...
        for (int i = 0; i < 5; i++)
        {
            Console.Write(".");
            Thread.Sleep(500); // Vizual üçün gecikmə
        }

        Console.WriteLine("\nƏsas axın tamamlandı!");
    }

    static void CountToTen()
    {
        for (int i = 1; i <= 10; i++)
        {
            Console.WriteLine($"[Axın] Sayırıq: {i}");
            Thread.Sleep(400);
        }
        Console.WriteLine("[Axın] Hazır!");
    }
}

Nə baş verir?

Konsolda nöqtələr və "Sayırıq: X" qarışıq şəkildə görünəcək. Bu — çoxaxınlılığın ilk əlamətidir! Əsas axın öz nöqtələrini yazır, yeni axın isə 10-a qədər sayır. Onlar bir-birinə mane olmadan, bir musiqi qrupundakı iki alətçi kimi işləyirlər: biri baraban, digəri pianino. Hər biri öz səsini verir və birlikdə yaxşılıq yaranır.

3. Axına necə məlumat ötürmək olar?

Bəzən axın nə edəcəyini bilməkdən başqa, nələrlə işləyəcəyini də bilməlidir. Əgər axın üçün metod parametr qəbul edirsə, onları necə ötürmək olar?

Variant 1: Lambda (anonim metod) vasitəsilə

int bounds = 7;
Thread t = new Thread(() => CountToNumber(bounds));
t.Start();
static void CountToNumber(int n)
{
    for (int i = 1; i <= n; i++)
    {
        Console.WriteLine($"[Axın] {i} / {n}");
        Thread.Sleep(300);
    }
}

Burada lazım olan metodu lambda içində çağırırıq ki, parametr ötürə bilək. Bu geniş yayılmış praktikadır, çünki Thread parametrsiz metodu (ThreadStart) gözləyir.

Variant 2: ParameterizedThreadStart-dan istifadə

ParameterizedThreadStart delegatından istifadə etmək olar, o bir object tipində bir parametr qəbul edir.

Thread t = new Thread(CountToNumberObject);
t.Start(12);

static void CountToNumberObject(object? n)
{
    int max = (int)n!;
    for (int i = 1; i <= max; i++)
    {
        Console.WriteLine($"[Axın] {i} / {max}");
        Thread.Sleep(200);
    }
}

Bəli, parametrin tipi object-dir, ona görə tip çevirməsi tələb olunur. Pisdir, amma işləyir! Müasir C# adətən lambda variantını üstün tutur.

4. Axının həyatının idarə edilməsi

Gəlin Thread sinfinin təqdim etdiyi faydalı şeyləri nəzərdən keçirək.

Xüsusiyyət / Metod Təyinat
Thread.Start()
Axını (yaradılarkən göstərilən metodu) işə salır
Thread.Join()
Axının bitməsini gözləyir (çağıran axını digər axının bitməsinə qədər bloklayır)
Thread.IsAlive
Axının hazırda işləyib-işləmədiyini göstərir (true/false)
Thread.Name
Axına ad verməyə imkan verir (debug üçün faydalıdır)
Thread.CurrentThread
Cari axını təmsil edən obyekt almaq
Thread.Sleep(ms)
Cari axını ms millisekund müddətinə dayandırır

Nümunə: Axının bitməsini gözləmək

Bəzən əsas axının köməkçi axının işinin bitməsini gözləməsi lazımdır.

Thread t = new Thread(() =>
{
    for (int i = 0; i < 5; i++)
    {
        Console.WriteLine($"[İkinci axın] {i}");
        Thread.Sleep(300);
    }
});
t.Start();

Console.WriteLine("[Əsas axın] İkinci axının bitməsini gözləyirik...");
t.Join(); // Əsas axın burada gözləyir

Console.WriteLine("[Əsas axın] İkinci axın bitdi!");

Join() olmadan proqram axın işləyərkən bitə bilər. Join() ilə əsas axın bütün işlər bitənə qədər səbrlə gözləyəcək.

5. Faydalı nüanslar

Axınların adlandırılması: qarışıqlığın qarşısını almaq üçün

Debug üçün axınlara ad vermək olar:

Thread t = new Thread(() =>
{
    Console.WriteLine($"Bu {Thread.CurrentThread.Name} axınında işlənir");
});
t.Name = "Sayma-Axını";
t.Start();

Bu, axınlar çoxaldıqda və hərəsinin öz işi olduqda kömək edəcək.

Məhdudiyyətlər və real gələcək

Dərhal demək lazımdır: müasir tətbiqlərdə əl ilə Thread ilə idarəetmə nadir hallarda istifadə olunur. Praktikada daha güclü və ağıllı alətlər (Task, async/await) geniş istifadə olunur, onlar barədə sonra ətraflı danışacağıq. Amma axınların iş prinsiplərini başa düşmək vacibdir:

  • C# və .NET-in "arxa planını" anlamaq üçün.
  • Müsahibələr üçün (bəzən sizdən Thread və Task arasındakı fərqi izah etməyiniz tələb olunur).
  • İrili-xırdalı, "miras qalmış" tətbiqlərdə mürəkkəb problemləri diaqnostika və düzəltmək üçün.

Nəticə sxemi: axının həyat dövrü

stateDiagram-v2
    [*] --> New: Thread yaradıldı
    New --> Running: Start()
    Running --> Stopped: Metod tamamlandı
    Stopped --> [*]

İndi siz C#-da axınları özünüz yaradıb işə sala bilirsiniz. Siz artıq sadəcə sərnişin deyilsiniz, bir neçə vaqonu idarə edən maquinistsiniz! Qarşıda — axının həyat dövrü, onun idarəsi, sinxronizasiya və paralelliyin yeni üfüqləri.

6. Thread ilə işləyərkən tipik səhvlər və hiylələr

Səhv №1: axını işə salmamaq.
Tez-tez Thread obyekti yaradılır, amma Start() çağırmaq unudulur. Nəticədə axın işə düşmür və bunun səbəbini dərhal tapmaq çətin ola bilər.

Səhv №2: ümumi məlumatları sinxronizasiya etmədən dəyişmək.
Bir neçə axın eyni dəyişənlə müdaxilə edirsə və müdafiə yoxdursa, problemlər gözləyin! Bu, iki kassirin eyni qutudan dəyişiklik verməsinə bənzəyir — tezliklə qarışıqlıq və səhvlər yaranar.

Səhv №3: köhnəlmiş və təhlükəli metodlardan istifadə.
Thread.Suspend(), Thread.Resume() və oxşar metodlardan istifadə etməyin — onlar təhlükəli və köhnə hesab olunur. Axınların həyat dövrünü başqa yollarla idarə edin.

Səhv №4: axın içində işlənməmiş istisnalar.
Axında tutulmayan istisna baş verərsə, axın bitəcək və əsas axın bunun haqqında xəbərsiz qala bilər! Axın kodunu try-catch blokuna alın ki, səhvləri tutub log edə biləsiniz.

Thread t = new Thread(() =>
{
    try
    {
        // ... sizin kodunuz
    }
    catch (Exception ex)
    {
        // Xətanı log edirik
        Console.WriteLine($"[Axın] Xəta: {ex.Message}");
    }
});
t.Start();
Şərhlər
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION