Samo par korekcija
- ThreadPool threadovi su OS level threadovi, thread pool ih kreira on demand, ali ih ne unistava (vidi dalje za v4), ne postoje magicni NET threadovi, sve je samo stvar manipulacijom koda koji se izvesava u tim threadovima. U .NET 4 ovo je cak i promenjeno u i sada Thread pool radi sa dva poola threadova za razne kategorije poslova (IO i custom) i unistava OS threadove u zavisnosti od "pritiska" na pool. U IO intensive managed aplikacijama broj OS level threadova moze da naraste fino. Na mom systemu GetMaxThreads vraca 1023+1000 (work+IO) za 32bit app, 32768+1000 za 64bit, sto znaci da u teoriji moze da kreira maksimalno 33768 OS threadova u samo jednom svom procesu. Tesko da se moze pricati o ThreadPoolu kao o "ekonomicnoj" verziji threadova
![](https://www.elitesecurity.org/images/static/emoticons/smile.gif)
Samim tim propada i ideja o tome da ThreadPool moze da spreci lose napisan async kod da kreira brdo threadova.
- Alokacija fizicke memorije pri kreiranju threadova nije 1Mb, to je max rezervisani virtuelni prostor za stek, alociran u virtuelnom adresnom prostoru. TO samo po sebi nije nikakav problem i ne predstavlja zauzece fizicke memorije, virtuelni adresni prostor za 64bit sistem je 8TB po procesu, za 32bit proces 3Gb (a kao sto vidis po gornjem max threadu, .NET je spreman da zauzem 2023Mb od te memorije samo za threadove, tako da ni usteda memorije nije rezon za pool). Commited deo steka je po defaultu 8kb od tih 1Mb i raste kako raste stek. Tu je naravno i TLS, ali sam .NET ne zadire u to, ako ga kod koristi, koristi.
- .NET nema svoj thread context switching, niti je uputno to raditi jer je OS thread context switiching sasvim ok. Samim tim je thread switching overhead identican za sve. Ono sto .NET ima je task scheduling, sto nije isto, task scheduling je proces redjanja taskova na ulaz u thread pool da bi se optimizovao broj aktivnih threadova i bezbedno cuvali taskovi koji su blokirani u await state-u. Takodje, ono sto .NET ima je EXECUTION context switching, sto nije isto sto i thread switching (execution switch omogucava transparentno prebacivanje izvrsenja sa jednog threada na drugi).
- async/await rade preko default thread pool-a, tako da isti ne oslobadjaju thread pool poslova, sta vise zavise od njega.
- async/await je c# jezicka konstrukcija oko kreiranja i manipulacije Task objektima, Task objekat se kreira i registruje u poolu cak i kad koristis async void. Ako disasemblujes kod videces da u osnovi sve radi preko Taskova:
- od async methode kompajler u osnovi pravi nesto sto se zove AsyncStateMachine privatna klasa, ciju instancu kreira pri "pozivu" async metoda koristeci AsyncTaskMethodBuilder a cija kompleksnost je defakto odredjena brojem "izlaznih" tacaka, tj brojem await tacaka u metodi.
- poziv async metode kreira gornji state machine i startuje ga na default thread poolu.
- await jednostavno uzima GetAwaiter iz taska koji se ceka i vidi da li je isti zavrsio, ako jeste menja state i nastavlja metod, ako nije markira poslednje zavrseni state, registruje task kao pod-task Taska kojeg ceka da bi rekao TaskScheduleru da ga ne ubacuje u ciklus dok cekani task ne zavrsi. Kad sledeci put udje u izvrsenje, sto znaci da je await odradio, state machine ce (koristeci switch semantiku) skociti na prvu sledecu komandu iza await.
- koga interesuje kako to izgleda nek pogleda klasu kroz .NET reflector koristeci IL prikaz, sve se lepo vidi.
- Task+Long Running ima identican efekat kao Thread, uz gubitak kontrole nad threadom:
https://coderkarl.wordpress.co...ong-running-tasks-and-threads/
- iz gornjeg razloga CLR nikad nece moci sam da deklarise Task kao long running.
- Thread treba da koristis umesto Task, imedju ostalog, kad je thread kritican i proces ne sme da se zavrsi dok se thread ne zavrsi. Po defaultu svi ThreadPool Task threadovi, ukljucujuci i kreirani long running, su background threadovi i ubijaju se po zavrsetku procesa. Drugi razlog je kad zelis da tvoj kod, cak i kad je blokiran, ostane u svom originalnom threadu. Posto thread pool koristi execution context switch, ne psotoji garancija da ce kod iza await biti izvrsen na istom threadu.
Prica je tu malo i komplikovanija kad se u pricu uvecde ExecutionContext, ali da ne tupimo do te mere. Generalno .NET ThreadPool i async/await su samo specijalizovana implementacija worker-crew paterna, nista manje nista vise, radi ispomoci programerima. Kao sto rece tdusko, ne postoji cak ni garancija da ce posao dobiti svoj thread i da nece biti uradjen na glavnom threadu, tvoj prvi sors npr kod mene daje ovaj rezultat:
Lorem
Ipsum
Dolor
DoSomething: False
DoSomething: 8
Main: 8
Sto znaci da je async rutina izvrsena sync, na glavnom threadu, tj nijedan await uDoSomething nije blokirao. Kad bih sebi dodao await Task.Delay(1) u metod, onda bi .NET uradio execution switch tog taska na worker thread i vratio se u Main, i dobio bih isti rezultat kao tvoj. To je i jedna od glavnih mana await/async mehnizma, ne ume da detektuje blocking mehanizam ako on nije awaitable (npr ubaci Thread.Sleep(10000) umesto await Task.Delay() i glavni thread ce biti blokiran 10 sekundi). Zato i .NET pere ruke od Taska i uvodi LongRunning flag
![](https://www.elitesecurity.org/images/static/emoticons/smile.gif)
pa ako imas sleep(10000), ti ga markiraj kao long running i dobices svoj thread
Samim tim, ako zaista ZELIS da nesto radi na drugom threadu, Thread objekat je jedino garantovano resenje.
[Ovu poruku je menjao mmix dana 04.01.2015. u 22:22 GMT+1]
Sloba je za 12 godina promenio antropološki kod srpskog naroda. On je od jednog naroda koji je bio veseo, pomalo površan, od jednog naroda koji je bio znatiželjan, koji je voleo da vidi, da putuje, da upozna,
od naroda koji je bio kosmopolitski napravio narod koji je namršten, mrzovoljan, sumnjicav, zaplašen, narod koji se stalno nešto žali, kome je stalno neko kriv… - Z.Đinđić