1. Giriş
Gəlin təsəvvür edək ki, threadlər dondurma üçün növbədə dayanan insanlardır. Kimsə sakitcə dayanır, kiməsə isə tələsikdir ("Mənim qatar tez gəlir, keçirəsiz!"), və satıcı hamını eyni şəkildə dinləyir kimi görünə bilər, amma bəzən kimsəni növbədən qabaq servis edir — məsələn, uşaq ağlayan ananı. Elə thread prioritetləri də təxminən belədir.
C#-də (daha dəqiq .NET-də) hər bir thread-in öz prioriteti var — bu əməliyyat sisteminə həmin thread-in digər thread-lərlə müqayisədə nə dərəcədə "vacib" olduğunu bildirən ipucu kimidir. Bu əməliyyat sistemi üçün ciddi qayda deyil (heç kim zəmanət vermir ki, ən yüksək prioritetli thread həmişə bütün diqqəti alacaq), amma adətən yüksək prioritetli threadlərə daha çox CPU vaxtı ayrılır.
Bu nə vaxt faydalıdır?
- İstifadəçi interfeyslərində: məsələn, ekran yenilənməsini uzun müddətli hesablamalarla yavaşlatmamaq üçün.
- Oyunlarda: kadrların render edilməsi fonda xəritə generasiya edilməsindən daha önəmlidir.
- Cavab vaxtı kritik olan tapşırıqlarda: avadanlıq idarəsi, siqnal emalı və s.
2. Thread prioritetləri: necə işləyir
C#-də thread prioriteti ilə işləmək sadədir — Thread obyektinin Priority xassəsi var. O, ThreadPriority enumerasiyasından qiymət alır:
| Qiymət | Təsvir |
|---|---|
|
Ən aşağı prioritet |
|
Orta səviyyədən aşağı |
|
Normal (default) |
|
Orta səviyyədən yuxarı |
|
Ən yüksək prioritet |
Kod nümunəsi:
using System;
using System.Threading;
class Program
{
static void Main()
{
Thread lowPriorityThread = new Thread(PrintLowPriority);
Thread highPriorityThread = new Thread(PrintHighPriority);
lowPriorityThread.Priority = ThreadPriority.Lowest;
highPriorityThread.Priority = ThreadPriority.Highest;
lowPriorityThread.Start();
highPriorityThread.Start();
}
static void PrintLowPriority()
{
for (int i = 0; i < 10; i++)
{
Console.WriteLine("Aşağı prioritet: " + i);
Thread.Sleep(10); // Fərqi görmək üçün fasilə əlavə edək
}
}
static void PrintHighPriority()
{
for (int i = 0; i < 10; i++)
{
Console.WriteLine("Yüksək prioritet: " + i);
Thread.Sleep(10);
}
}
}
Qeyd
Praktikada, xüsusən müasir çoxnüvəli sistemlərdə və idarə olunan .NET mühitində prioritet sadəcə əməliyyat sisteminə tövsiyədir. OS (və .NET) yüksək prioritetli thread-ə daha çox vaxt ayırmağa çalışır, amma sərt zəmanətlər yoxdur. Məsələn, əgər sizin thread bütün CPU resursunu Highest prioritetində götürsə, interfeys donmağa başlaya və digər thread-lər əziyyət çəkə bilər.
3. Prioritetlərin dəyişdirilməsi
Threadlər və prioritetlər
graph LR
A[Thread 1 - Lowest] -->|Az CPU vaxtı| OS(Əməliyyat sistemi)
B[Thread 2 - Normal] --> OS
C[Thread 3 - Highest] -->|Daha çox CPU vaxtı| OS
OS --> CPU(Processor)
Niyə prioriteti dəyişdirmək lazımdır?
Hər şeyi Highest qoymaq həmişə ağlabatan deyil! Təsəvvür edin, bütün mağaza müştəriləri eyni anda "Məni əvvəlcə xidmət edin!" deməyə başlayar.
Prioriteti yalnız əsaslı səbəb olduqda dəyişin:
- Log yazan fon thread — BelowNormal və ya Lowest edə bilərsiniz.
- İstifadəçi girişini emal edən thread — AboveNormal.
- CPU-intensiv, vaxt baxımından kritik olmayan işlər — aşağı prioritet.
Praktik məsləhət: Əgər tətbiq yavaşlayırsa, yoxlayın görək "tələbkar" yüksək prioritetli threadlər varmı.
4. .NET-də thread kateqoriyaları
C#-də ənənəvi olaraq iki növ thread ayırırlar: foreground və background. Maraqlıdır ki, "background" sözündən nəzərdə tutulan iş həmişə "fon" işi deyil. İndi izah edək.
Foreground threadlər (əsas)
- Default olaraq sizin yaratdığınız threadlərin əksəriyyəti foreground olur.
- Prosesdə ən azı bir foreground thread yaşadığı müddətcə tətbiq bitməyəcək, hətta digər hər şey "ölü" olsa belə.
- Məsələn: proqramın əsas thread-i (Main), həmçinin new Thread() ilə yaradılan threadlər əgər dəyişdirilməyibsə.
Background threadlər (köməkçi)
- Əgər bütün foreground threadlər bitibsə və yalnız background threadlər qalıbsa — proses sadəcə bitir və background threadlər xəbərdarlıq olmadan dayandırılır.
- Onlar köməkçi işlər üçün istifadə olunur: loglama, fon metric göndərmə və s.
- Thread-i background etmək üçün IsBackground xassəsini true qoyun:
Thread t = new Thread(SomeMethod);
t.IsBackground = true;
t.Start();
Fərqi göstərən nümunə
using System;
using System.Threading;
class Program
{
static void Main()
{
Thread backgroundThread = new Thread(() =>
{
for (int i = 0; i < 10; i++)
{
Console.WriteLine("Background thread işləyir... " + i);
Thread.Sleep(500);
}
Console.WriteLine("Background thread SONLANDI.");
});
backgroundThread.IsBackground = true; // Thread-i background edirik!
backgroundThread.Start();
Console.WriteLine("Main 1 saniyə sonra işi bitirəcək.");
Thread.Sleep(1000); // Bir saniyə gözləyirik
Console.WriteLine("Main bitdi. Background thread ilə nə olacaq?");
}
}
Nə görəcəksiniz?
Main bitə bilər və background thread yarımçıq dayandırılacaq. Bu, tətbiqin bağlanmasına mane olmamalı olan işlər üçün faydalıdır.
5. Thread növləri
ThreadPool (Thread hovuzu)
- ThreadPool — çox sayda thread yaratmamaq üçün thread hovuzunu idarə edən mexanizmdir.
- Qısa müddətli çoxsaylı işlər üçün istifadə olunur: paralel sorğu emalı, asinxron əməliyyatlar.
- Hovuzdan götürülən threadlər həmişə background olar — onlar tətbiqin bağlanmasına mane olmur.
Nümunə: Thread hovuzunda kod işlətmək
using System;
using System.Threading;
class Program
{
static void Main()
{
ThreadPool.QueueUserWorkItem(DoWorkInThreadPool, "fon işi");
Console.WriteLine("Main işi bitirir.");
Thread.Sleep(500);
}
static void DoWorkInThreadPool(object? state)
{
Console.WriteLine("Hovuz threadi: " + state);
Thread.Sleep(1000); // Yatmağa çalışaq
Console.WriteLine("Hovuz threadi sona çatdı!");
}
}
Nəzərə alın:
Əgər Main əvvəl bitərsə, hovuz threadi kəsilə bilər. Tamamlanmaya zəmanət lazımdırsa — foreground threadlərdən istifadə edin və ya açıq şəkildə tamamlanmanı gözləyin.
Multithreading-in inkişafı: Task, async/await
Müasir tətbiqlərdə əksər hallarda əl ilə thread yaratmaq (Thread) az istifadə olunur. Adətən Task və asinxron metodlardan istifadə edilir. Vacib məqamlar:
- Hovuz threadləri və Task-lər background threadlərində işləyir.
- Çox hallarda prioriteti dəyişmək lazım deyil — sağlam məntiqə və best practice-lərə əməl edin.
6. Faydalı nüanslar
Thread xassələri və davranış
| Xassə | Foreground Thread | Background Thread | ThreadPool Thread |
|---|---|---|---|
|
false default olaraq | true | true |
| Tətbiqin bitməsi | Tətbiq foreground threadlərdən ən azı biri yaşadığı müddətcə bitmir | Tətbiq bütün foreground threadlər bitəndən sonra tamamlanır | Tətbiq Main bitən kimi tamamlanır |
| İdarəetmə | Tam nəzarət | Tam nəzarət | Birbaşa nəzarət yoxdur |
| Harada istifadə olunur | Uzunmüddətli, vacib işlər (məsələn, DB server) | Əhəmiyyətsiz işlər, fon əməliyyatları, loglama | Qısa müddətli işlər, Task, asinxron əməliyyatlar |
| Prioritet | Təyin etmək mümkündür | Təyin etmək mümkündür | Dəyişdirmək tövsiyə olunmur |
Maraqlı məqamlar və xəbərdarlıqlar
- Prioritet yalnız adi threadlər (Thread) üçün dəyişdirilə bilər, hovuzdan gələn işlər (Task, ThreadPool) üçün yox.
- Hovuz threadləri həmişə Normal prioritetinə malikdir (onu dəyişmək olmaz).
- Task və async/await daha müasir yanaşmadır və prioritetlər və fon icra məsələlərini "kulislərin arxasında" gizlədir.
7. Prioritetlər və thread növləri ilə bağlı tipik səhvlər
Səhv №1: prioritetləri sui-istifadə etmək.
Bütün threadlərə səbəbsizcə Highest və ya hamısına Lowest təyin etmək fayda gətirməz. Bu işlərin icra balansını poza və tətbiqin cavabvericiliyini azalda bilər.
Səhv №2: background threadlərin gözlənilmədən dayandırılması.
Əgər vacib iş (məsələn, məlumatların saxlanması) background threaddə gedirsə və siz onun tamamlanmasını gözləmirsinizsə, məlumatların itməsi riski var. Background threadlər proses bitəndə avtomatik dayandırılır.
Səhv №3: prioritetdən yüksək nəticələr gözləmək.
Thread prioriteti (Priority) əməliyyat sisteminə tövsiyədir, amma thread-in ilk icra olunacağına zəmanət deyil.
GO TO FULL VERSION