CodeGym /Kurslar /C# SELF /Müqayisə Task və <...

Müqayisə TaskThread

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

1. Giriş

İndi vaxtıdır başa düşək — TaskThread prinsipcə nədə fərqlənir? Niyə C# illərdir ki, birbaşa thread idarəçiliyinə deyil, Task-lara üstünlük verir? Hansı situasiyalarda əl ilə thread yaratmağa davam etmək olar, hansı hallarda isə task-larla işləmək kifayətdir (və lazımdır)?

Əgər "thread" və "task" sözləri beyninizin qaranlıq guşəsində bir az qarışır və ürəyiniz daha sürətlə döyünürsə — narahat olmayın, tək deyilsiniz. Hətta təcrübəli proqramçılar da paralellik və asinxronluq mövzusunda bəzən çaşırlar.

Gəlin hər şeyi rəflərə düzək. Başlayaq!

Task-ın yaranmasının qısa tarixi

Keçmişdə (.NET 4.0-dan əvvəl) paralel və ya "fon" rejimində kod işlətməyin ən açıq yolu yeni thread yaratmaq idi. Məsələn, new Thread(() => { ... }).Start(); Thread-lər sadədir. Amma onların pis tərəfi odur ki, hər şey sizin çiyninizdədir. Resurs ayırma, həyat dövrü, istisna emalı, sinxronizasiya, monitorinq, miqyaslana bilənlik — bunların hamısı developer-in qayğısıdır. Proqramlaşdırmada bir az tembellik yaxşıdır!

Vəziyyət Task ilə dəyişdi — bu, System.Threading.Tasks.Task adlanan namespace-dən gəlir. Task — thread deyil. Bu daha abstrakt və çevik anlayışdır. Gələcəkdə (bəlkə paralel) icra olunacaq işi təsvir edir.

2. Thread — "Sade thread"

Thread — aşağı səviyyəli icra vahididir, əməliyyat sisteminin ayrılmış resurslarını (öz stack-i, icra kontekstini və s.) təmsil edir. Əgər thread-i əl ilə yaradırsınızsa, onun işə düşməsi, bitməsi və həyatının bütün incəlikləri sizin məsuliyyətinizdir.

using System;
using System.Threading;

class Program
{
    static void Main()
    {
        Thread thread = new Thread(() => {
            Console.WriteLine("Salam thread-dən!");
        });

        thread.Start();
        thread.Join(); // Thread-in bitməsini gözləyirik
    }
}
  • Burada biz thread yaratdıq və o öz stack-də lambda icra edir.
  • Thread-i işə saldıqdan sonra onun bitməsini gözləmək üçün Join() çağırırıq.

Nəyin pərdəarxası var?

  • Hər bir thread yaddaş tutumuna malikdir (stack, təxminən 1 MB).
  • .NET-də minlərlə thread-i əl ilə yaratmaq tövsiyə olunmur — sistem əziyyət çəkər.
  • Join() çağırmağı unutmusunuzsa, əsas thread uşaqdan əvvəl bitə bilər və proqram "kəsilə" bilər.
  • Thread daxilindəki istisnalar avtomatik yuxarı çıxmır — onları xüsusi şəkildə tutmaq lazımdır!
  • Thread-i "gözəl" şəkildə ləğv etmək çətindir (heç bir Stop() metodu yoxdur!).

3. Task — "Yeni nəsil task-lar"

Task — daha ağıllı abstraksiyadır, "gələcəkdə icra olunacaq iş"i təmsil edir. Task-lar adətən ThreadPool üzərində işləyir, bu da çoxlu sayda thread yaratmaqdan daha effektivdir. Siz onların yaradılmasını əl ilə idarə etmirsiniz, pool sizin üçün bunu sürətli və ağıllı şəkildə edir, yüklənməyə uyğun olaraq thread sayını miqyaslayır.

using System;
using System.Threading.Tasks;

class Program
{
    static async Task Main()
    {
        Task task = Task.Run(() =>
        {
            Console.WriteLine("Salam Task-dan!");
        });

        await task; // Task-in bitməsini gözləyirik
    }
}
  • Burada task mütləq ayrı bir thread-də işə düşəcəyini zəmanət etmir, amma adətən pool-dan bir thread istifadə olunur.
  • Task-in bitməsini await ilə asanlıqla gözləyə bilərsiniz (asinxron metodda) və ya sinxron kontekstdə task.Wait() ilə.

4. TaskThread arasındakı fərq nədir?

Gəlin fərqləri sıraya düzək — nə üçün hansı məqsədlə istifadə olunur və hansı (görünməz) tələlər var.

Thread Task
Abstraksiya OS thread-i İş/Task (abstraksiya, hansı ki mümkündir thread istifadə etsin)
İşə salma Through new Thread(...).Start() Through Task.Run(...), Task.Factory.StartNew(...), async-metodlar
Birbaşa idarəetmə Bəli (start, Join, prioritet və s.) Yox, .NET idarə edir
Thread pool Xeyr, thread hər dəfə yeni yaradılır Bəli, adətən ThreadPool istifadə olunur
Resursların idarəsi Öz stack ayrılır Resurslar pool tərəfindən reuse olunur
Miqyaslana bilənlik Pis: 1000+ thread üçün səmərəsiz Ağıllı: minlərlə task yaxşı işləyir
İnteraksiya OS səviyyəsində ayrı thread Hazırkı thread-in davamı ola bilər, ya da ThreadPool-da ola bilər
İstisnalar Səlahiyyətli tutulmalıdır, əks halda "itə bilər" İstisnalar Task-də saxlanılır; onları await və ya .Wait() zamanı tuta bilərsiniz
Ləğv etmə Standart yol yoxdur Bəli, CancellationToken vasitəsilə dəstək var
İşin nəticəsini almaq Join() ilə gözləmək await, .Wait(), .Result
İstifadə məqsədi Xüsusi hallar — UI thread-ləri, long-lived thread-lər Daha çox fon/paralel işlər üçün

5. Nə vaxt nədən istifadə etmək?

Qəbul edilə bilər ki, Thread nə vaxt?

Açığı desəm, müasir .NET kodunda əl ilə thread yaratmaq olduqca nadir hallarda tələb olunur. Aşağıdakı hallarda məntiqlidir:

  • Çox uzun müddət işləyən thread yaratmaq lazımdır (məsələn, radio siqnalının serializasiyası və ya hardware-dən məlumat emalı) və bu thread "xüsusi" olmalıdır: aşağı prioritet, öz culture, ayrıca ad.
  • Bəzi aşağı səviyyəli API-lərlə inteqrasiya zamanı, hansı ki manual thread idarəsi tələb edir.
  • Ciddi spesifik hallarda, məsələn özəl task scheduler-lər yaradarkən.

Qalan bütün hallarda — Task daha düzgün və müasir seçimdir.

Task nə vaxt?

Təxminən həmişə, iş "fon"da və ya paralel icra olunmalıdırsa:

  • Pula uyğun olan hər cür fon hesablama (məsələn server sorğusunun emalı, fayl parsing, mail göndərmə).
  • Asinxron əməliyyatların işə salınması (async/await) — mexanizm Task və ya Task<T> qaytarır.
  • Task-ların birləşdirilməsi, davamlılıqların (continuations) emalı, zəncirlərlə işləmək.
  • Ləğv etmənin, gözləmə və nəticələrin toplanmasının sadəliyi: Task CancellationToken-u dəstəkləyir və müasir API-larla yaxşı inteqrasiya olunur.
  • Asinxron I/O əməliyyatları: şəbəkə sorğuları, fayllarla işləmə, verilənlər bazası əməliyyatları.

Müqayisə

Ssenari Thread Task
Long-lived thread (məsələn, öz servisin) Bəli Xeyr
Qısa tapşırıqların kütləvi icrası Xeyr Bəli
Asinxron I/O-əməliyyatları (await) Xeyr Bəli
Task-ların birləşdirilməsi, ləğvi, zəncirlər Xeyr Bəli
Prioritet və culture-in incə tənzimlənməsi Bəli (amma nadir) Xeyr, standart task-lar üçün yox
CPU arasında sadə iş bölgüsü Bəzən Bəli

6. Faydalı nüanslar

Task həmişə thread deyil!

Ən güclü magiya budur: əgər siz Task-ı asinxron I/O əməliyyatları üçün istifadə edirsinizsə, yeni thread ümumiyyətlə yaradılmır! Hər şey IO Completion Ports və ya digər platform primitivləri üzərində "səssizcə" gedir. Gözləmə vaxtı heç bir thread məşğul olmur!

Task və asinxronluq (I/O-bound) — await magiyası

using System;
using System.Net.Http;
using System.Threading.Tasks;

class Program
{
    static async Task Main()
    {
        // Asinxron olaraq saytın məzmununu yükləyirik (I/O-bound)
        HttpClient client = new HttpClient();
        string data = await client.GetStringAsync("https://www.dotnetfoundation.org");
        Console.WriteLine($"Alınan simvolların sayı: {data.Length}");
    }
}
  • Burada task (Task<string>) asinxron I/O əməliyyatını inkapsulyasiya edir.
  • Thread bloklanmır — o işləməyə davam edir, yükləmə bitəndə metodun icrası davam edir.
  • Belə iş üçün əl ilə thread yaratmaq tamamilə artıq və səmərəsizdir.

TaskThreadPool

Siz Task.Run(...) yazanda və ya asinxron API istifadə edəndə (await), .NET adətən xüsusi thread pool — ThreadPool-u istifadə edir. Bu, əvvəlcədən yaradılmış və ehtiyatda gözləyən thread-lərin toplusudur ki, onlar hər hansı gələn işi tez cəlb edə bilirlər. Az yüklə thread-lər boşda qalır, çox yüklə isə yeni thread-lər ağıllı şəkildə artırılır. Nəticədə tətbiqləriniz task sayı üzrə miqyaslana bilir, sistemə artıq yük düşmür.

new Thread ilə yaradılan thread adətən sistemdə ayrıca "sakin" olur — iş bitəndən sonra pool-a qayıtmır, sadəcə ölür. Bu səbəbdən Task mass-paralellük üçün daha səmərəlidir.

7. Tipik səhvlər və tələlər

Əgər qəflətən retro-programmist olmaq və hər şeyi thread-lərlə yazmaq qərarına gəlsəniz, gözəl macəralar sizi gözləyir: yaddaş sızmaları, mürəkkəb sinxronizasiya, işin ləğv edilə bilməməsi, "asılı" thread-lər (zombi proseslər), istisnaların tutularaq emalı üçün xüsusi API-lərdən istifadə ehtiyacı.

Ən əsası yadınızda saxlayın: "Task" — rahatdır, təhlükəsizdir və müasir tərzdədir. C# ilə işləyərkən əksər hallarda manual thread idarəsinə qayıtmağa heç bir səbəb yoxdur.

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