MultiThreading, asinxron proqramlaşdırma, parallelism, concurrency — bunların fərqi nədir?

Hasan Shans
6 min readJun 5, 2020

--

Salam! Bugün mən sizlərə multithread, asynchronous programming, parallelism, concurrency anlayışlarının fərqini izah etməyə çalışacam.

Məqalədə Thread, ThreadPool nədir, Task nədir və s. bu tipli suallardan bəhs edilməyəcək, bu məqalə artıq bu göstərdiklərimi bilib də, bunu daha dərindən anlamaq və ən əsas asinxron proqramlaşdırma ilə multithreading fərqini daha aydın izah etmək üçün nəzərdə tutulub.

“Task” yazmaqla .NET-ə göz vursam da, burada heç bir kod göstərilmir, çünki mövzu koda bağlı deyil, sadəcə yuxarıda göstərilən anlayışların mahiyyətini izah etmək üçündü.

Suallar:

  1. Multithread, asynchronous programming, parallelism, concurrency — nədir?
  2. CPU-bound və I/O-bound nə deməkdi?
  3. Context switch nədir?
  4. Best Practices

Və beləliklə, başladıq…

1) Multithread, asynchronous programming, parallelism, concurrency — nədir?

Kod yazdığınız zaman praktiki olaraq istəlinən əməliyyatı növbəti 4 şəkildə yerinə yetirmək olar:

  1. Single Thread — Synchronous
  2. Single Thread — Asynchronous
  3. Multithread — Synchronous
  4. Multithread — Asynchronous

Əvvəlcə misallarla başlayaq ki, anlamaq asan olsun.

Deyək ki, evinizə qonaqlar gəlib, siz onlar üçün stol açmalısız. Bu proses 4 fərqli əməliyyatlardan ibarətdi:

1) Çayı qaynamağa qoymaq: GetTea()
2) Salat hazırlamaq: MakeSalad()
3) Düşüb aşağıdan çörək almaq: BuyBread()
4) Stolun üstünə yeni süfrə salmaq: RefreshTable()

Gəlin indi yuxarıda göstərilən hər 4 modeli bu misal üstündə tətbiq edək. Misala görə, proseslərdə iştirak edən hər bir adam — bir thread deməkdi.

P.S

Əslində bu bənzətmə thread sayı CPU nüvəsi (core) sayı ilə eyni olduğunda doğru sayılır. Bu barədə az sonra.

Single Thread — Synchronous:
Single Thread olduğu üçün, prosesləri tək adam görəcək. Əvvəlcə biz çaydanı qaynamağa qoyuruq, gözləyirik qaynayıb bitsin, daha sonra salat hazırlayırıq, ondan sonra düşüb çörək alırıq və nəhayət evə qayıdıb süfrəni dəyişirik.

Single Thread — Asynchronous:
Bu yanaşmada da bayaqki əməliyyatlar yerinə yetiriləcək. Amma biz bunları sinxron olaraq deyil, asinxron edəcəyik. Məsələn, çaydanı qaza qoyduqdan sonra onun qaynamasını gözləmək əvəzinə, digər işlərlə məşqul oluruq. Belə olan halda “context switch” baş verir (az sonra). Çaydan qaynayıb bitəndən sonra artıq qayıdıb, result alırıq — çay süzürük.

Multithread — Synchronous:
Bu model əslində şəxsən mənim üçün çox mənasız görsənir. İnanmıram, haradasa yararlı olsun. Thread bizim misalda adam sayına bərabərdisə, multithread o deməkdir ki, artıq bizimlə bir neçə nəfər olacaq və hərə bir iş görəcək, AMMA SİNXRON ŞƏKİLDƏ. Yəni, mən çaydanı yandırıb, gözləyəcəm qaynasın, bitdiktən sonra siqnal verəcəm o biri yoldaşa ki, salata başlasın, o da bitdikdən sonra o birinə deyəcək çörək alsın və s. Amma biz nəyə görə bir birimizi gözləməliyik ki?

Multithread — Asynchronous:

Məgər daha yaxşı olmazmı ki, mən salat hazırlayana qədər, digər yoldaş çörək alsın, o biri də süfrə salsın? — OLAR! Elə məhz bu MULTITHREAD ASYNCHRONOUS modelidi. Elə parallel proqramlaşdırma (parallelism) da budur! Başqa dillə desək — fantastika!

2) CPU-bound və I/O-bound (input/output) nədir?

CPU-bound prosessora bağlı olan əməliyyatlardı. Yəni, məsələn siz böyük hesablamalar edirsizsə, 1-dən milyarda qədər for işə salırsızsa — bu böyük vaxt və enerji tələb edən əməliyyatlar olacaq və prosessor yüklənəcək.

I/O-bound isə məlumat mübadiləsi zamanı baş verir. Yəni, məsələn bazaya sorğu göndərərkən, siz sadəcə cavab gözləyirsiz, başqa heçnə. Heç bir böyük əməliyyat aparılmır.

Bizim misalımızda “çaydanı qaynamağa qoymaq” — I/O-bound sayılır. San ki, bazaya sorğu göndərirsiz və result gözləyirsiz.
“Qaynadıqdan sonra çay süzürsüz”.

Digər əməliyyatlar isə CPU bağlı olanlardı. Salat hazırlamaq, çörək almaq, süfrə salmaq — vaxt və enerji tələb edir, heç birində heçnə gözləmirik, əməliyyat yerinə yetiririk.

3) Context Switch nədir?

Əslində, siz nə qədər thread yaratsaz da parallel olaraq eyni zamanda yalnız CPU-nun nüvə sayı qədər thread işləyə bilər. Əgər 2 nüvə varsa, thread sayı isə 3-dürsə, həqiqətən eyni zamanda (parallel) yalnız 2 thread işləyəcək. Qalan thread-lər üçün context switch baş verəcək. Bu nə deməkdi?

Məsələn, deyək ki, biz 3 əməliyyat yerinə yetirməliyik və hərəsi üçün ayrıca thread yaradırıq. Nüvə sayımız isə cəmi 2 dənədi. Belə olan halda, thread-lər arasında müəyyən vaxt kəsikləri ilə keçidlər edilərək, əməliyyatlar tikə-tikə müxtəlif thread-lərdə yerinə yetiriləcək. Bu vaxt kəsikləri — kvant vaxtı adlanır. Keçidlər isə — context switch. CPU nüvəsi thread-ləri dəyişərək biraz 1-ci əməliyyatı edir, dayanır, keçir 2-ci əməliyyatı biraz görür, sonra təzədən birə oppanır — və belə bitənə kimi.

Misalımıza başlamazdan qabaq mən “misaldaki insan sayı thread sayına bərabərdi” yazmışdım. Sizi çaşdırmamaq üçün bir şeyi qeyd etməmişdim: bu bənzətmə CPU nüvəsi sayı ilə thread sayının eyni olduğu halda keçərlidir. Əslində, “insan sayı CPU nüvəsi sayına bərabər olmalıdı”. Bu o deməkdir ki, əgər görüləcək 3 əməliyyat varsa (və hər bir əməliyyat üçün bir thread yaradılırsa), siz isə 2 nəfərsizsə (2 nüvəli CPU), o zaman context switch baş verəcək.

Yuxarıdaki şəkil, iki nüvəli CPU iki əməliyyatı (2 thread-də) yerinə yetirərkən baş verir! Yox əgər, əməliyyat sayı (thread sayı) nüvə sayından çox olarsa, əməliyyatlar thread-lər arasında tikələrə bölünür (aşağıdaki şəkildə bir nüvə var).

Qayıdaq misalımıza. Təsəvvür edin bayaqki işləri görmək lazımdı, amma bu dəfə 2 nəfər ilə (2-nüvəli CPU):

1) Salat hazırlamaq
2) Düşüb aşağıdan çörək almaq
3) Stolun üstünə yeni süfrə salmaq

(“çay qaynatmaq” CPU-bound əməliyyat olmadığı üçün yazmıram)

Belə olan halda, sizin biriniz (bir nüvə, thread yox) düşəcək çörək dalınca, o birisi iki işi birdən görəcək (2 thread arasında tikələrə bölərək). Təxmini belə bir ssenari alınır:

Burada 2 əməliyyat (Task) var, amma bir nüvə, ona görə hissə-hissə ikisini də bir nüvə yerinə yetirir (task-lar hər dəfə dəyişəndə context switch baş verir və thread-lər dəyişir - öncəki şəkilə bax).

Əməliyyat bir nüvə ilə (bir adam tərəfindən), lakin tikələrə bölünərək 2 thread içində asinxron baş verir (context switch ilə):

Əvvəl xiyarı doğrayır — kontekst dəyişir — köhnə süfrəni çıxardır. Sonra pomidoru doğrayır, yeni süfrəni şkafdan çıxarır (yenə kontekst dəyişdi). Daha sonra xiyar pomidoru qarışdırır və yeni süfrəni salır stola. Əməliyyat uğurla başa çatdı. (şərt deyil eyni vaxtda bitsin, məsələn, salata hələ duz da vurmaq lazım ola bilər) İki əməliyyat eyni vaxtda yerinə yetirilir, amma parallel yox! Eyni anda dostumuz həm salat doğrayıb, həm də süfrə sərmir. Amma birini qurtarmamış o birinə tullanır və beləcə iki işi birdən görür.

Yəni, bir nüvə asinxron olaraq iki işi — müəyyən zaman kəsiyi ilə bir thread-dən o birinə tullana-tullana (context switch) — eyni vaxtda görür. CONCURRENCY elə budur!

İki CPU iki işi eyni anda, heç bir thread-lər arası keçid olmadan edirsə — bu olur PARALLELİSM.

4) Best Practices:

1) Asinxron — Multithread demək deyil!

Xeyir! Siz çaydanı yandırıb, başının üstündə qaynamağını gözləyib, sonra başqa işləri görürsünüzsə — siz single thread ilə sinxron əməliyyatlar aparırsız. Yox əgər çaydanı yandırıb, qaynamağı gözləmədən başqa işləri görürsünüzsə — SİNGLE THREAD ASİNXRON yanaşması seçmiş olursuz, çünki bayaq dediyimiz kimi, thread-lər bizim misalda insan sayına bərabərdi (CPU nüvəsi sayı ilə eyni olduqda). Biz təkik. Thread təkdi. Amma asinxron hərəkət edirik. Ona görə asinxron və multithread — eyni şey deyil! Bir thread ilə də asinxron modeli tətbiq etmək olar!

2) I/O əməliyyatları ASİNXRON yerinə yetirmək performansı artırır, AMMA MULTITHREAD — azaldır!

Yəni, çaydanı yandırıb, gözləməyib (asinxron) digər işləri görmək daha yaxşıdı. Bir adam (thread) kifayət edir. Axı siz niyə dostunuzu (başqa thread-i) məşqul edib, “çaydanı yandır, qaynamağını gözlə” deyə komanda verəsiz? Ondansa başqa daha vacib işlərlə məşqul olar, deyil mi? :)

3) Mürəkkəb CPU-bound əməliyyatlarda isə asinxron multithread yanaşması performansı artırır:

Əgər görüləcək iş çoxdursa bunu eyni zamanda (asinxron) dostlarınızla (bir neçə thread ilə) etsəz daha rahat olmaz mı?! Amma unutmayaq ki, hər xırda şeyə görə thread yaratmaq düzgün deyil. Siz dostlarınızı otağın lampasını dəyişmək üçün çağırıb, məşqul edərsiz?

Abunə olun, növbəti məqalələri qaçırmayın :)

References:

  1. https://medium.com/swift-india/concurrency-parallelism-threads-processes-async-and-sync-related-39fd951bc61d
  2. https://medium.com/@aliakhtar_16369/concurrency-in-swift-grand-central-dispatch-part-1-945ff05e8863
  3. https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/async/
  4. CLR via C# kitabı

--

--

Hasan Shans
Hasan Shans

Written by Hasan Shans

Junior Philosopher & Polyglot.

No responses yet