Utilizând System.Threading.Tasks.Parallel creați un fir nou în pool-ul de fire?

Poate că nu am înțeles-o corect ... toate probleme de clasă Parallel :(

Dar, din ceea ce citesc acum, înțeleg că atunci când folosesc paralela, eu efectiv mobilizează toate firele care există în threadPool pentru o anumită sarcină/misiune.

For example:

  var arrayStrings = new string[1000];
  Parallel.ForEach(arrayStrings, someString =>
  {
       DoSomething(someString);
  });

Deci Parallel.ForEach în acest caz mobilizează toate firele care există în threadPool pentru sarcina/doza "DoSomething".

Dar convorbirea Parallel.ForEach va crea deloc un thread nou?

Este clar că nu vor exista 1000 de fire noi. Dar sa presupunem ca exista 1000 de thread-uri noi, cateva cazuri in care threadPool elibereaza tot threadul pe care-l detine asa, in acest caz ... Parallel.ForEach va crea orice thread nou?

6
Parallel.ForEach - "Execută o operație foreach (pentru fiecare în Visual Basic) în care iterațiile pot să fie difuzate în paralel."
adăugat autor Damien_The_Unbeliever, sursa

4 răspunsuri

Răspuns scurt: Parallel.ForEach() nu "mobilizează toate firele". Și orice operațiune care programează unele lucrări pe ThreadPool (care Parallel.ForEach() ) poate provoca crearea unui fir nou în pool.

Răspuns lung: pentru a înțelege acest lucru în mod corespunzător, trebuie să știți cum funcționează trei niveluri de abstractizare: Parallel.ForEach() , TaskScheduler și ThreadPool

  1. Parallel.ForEach() (and Parallel.For()) schedule their work on a TaskScheduler. If you don't specify a scheduler explicitly, the current one will be used.

    Parallel.ForEach() splits the work between several Tasks. Each Task will process a part of the input sequence, and when it's done, it will request another part if one is available, and so on.

    How many Tasks will Parallel.ForEach() create? As many as the TaskScheduler will let it run. The way this is done is that each Task first enqueues a copy of itself when it starts executing (unless doing so would violate MaxDegreeOfParallelism, if you set it). This way, the actual concurrency level is up to the TaskScheduler.

    Also, the first Task will actually execute on the current thread, if the TaskScheduler supports it (this is done using RunSynchronously()).

  2. The default TaskScheduler simply enqueues each Task to the ThreadPool queue. (Actually, it's more complicated if you start a Task from another Task, but that's not relevant here.) Other TaskSchedulers can do completely different things and some of them (like TaskScheduler.FromCurrentSynchronizationContext()) are completely unsuitable for use with Parallel.ForEach().

  3. The ThreadPool uses quite a complex algorithm to decide exactly how many threads should be running at any given time. But the most important thing here is that scheduling new work item can cause the creating of a new thread (although not necessarily immediately). And because with Parallel.ForEach(), there is always some item queued to be executed, it's completely up to the internal algorithm of ThreadPool to decide the number of threads.

Așadar, este destul de imposibil să se decidă câte fire vor fi folosite de un Parallel.ForEach() , deoarece depinde de multe variabile. Ambele extreme sunt posibile: bucla va rula complet sincron pe firul curent și că fiecare element va fi rulat pe firul propriu, nou creat.

Dar, în general, ar trebui să fie aproape de eficiența optimă și, probabil, nu trebuie să vă faceți griji cu privire la toate aceste detalii.

11
adăugat

Parallel.Foreach nu creează fire noi și nici nu "mobilizează toate firele". Utilizează un număr limitat de fire din filetul thread și le trimite sarcini pentru executarea paralelă. În implementarea actuală, implicit este utilizarea unui fir pe nucleu.

1
adăugat
Pur și simplu nu este adevărat. Dacă codul din Parallel.ForEach() blochează sau rulează pentru o lungă perioadă de timp, se vor folosi mai multe fire decât numărul de nuclee.
adăugat autor svick, sursa

I think you have this the wrong way round. From PATTERNS OF PARALLEL PROGRAMMING you'll see that Parallel.ForEach is just really syntactic sugar.

Parallel.ForEach este în mare măsură fiert în jos la ceva de genul asta,

for (int p = 0; p < arrayStrings.Count(); p++)
{
    ThreadPool.QueueUserWorkItem(DoSomething(arrayStrings[p]);
}

ThreadPool se ocupă de planificare. Există câteva articole excelente despre modul în care planificatorul ThreadPool se comportă într-o oarecare măsură dacă sunteți interesat, dar asta nu are nimic de-a face cu TPL.

1
adăugat
Parallel.ForEach() nu este construit pe ThreadPool , este construit pe TaskScheduler . De asemenea, este mai inteligent în legătură cu codul din fiecare Task , astfel încât să nu existe o singură Task pentru fiecare element. Un alt lucru este că codul dvs. nu va bloca, dar Parallel.ForEach() face.
adăugat autor svick, sursa
@AllonGuralnek esti corect, actualizat.
adăugat autor M Afifi, sursa
new Thread() va crea întotdeauna un thread nou, nu va folosi unul din filepoolul thread. Codul pe care l-ați postat va crea întotdeauna cât mai multe fire decât cele din colecție. Acest lucru nu reprezintă deloc Parallel.ForEach.
adăugat autor Allon Guralnek, sursa

Paralel nu se ocupă deloc cu firele - planifică SARCINI în cadrul sarcinii. Apoi, acesta are un programator și planificatorul implicit merge la filetul de filet. Acesta va incerca sa gaseasca un numar mare de thread-uri (mai bine in 4,5 decat 4,0) si Threadpool-ul ar putea inversa lent noi fire.

Dar asta nu este o functoin de paralel.

Parallel.ForEach va crea orice fir nou

Nu o va face niciodată. Așa cum am spus - are 1000 de foreach, atunci cade 10.000 de sarcini, Point. Planificatorul fabrica de sarcini va face ceea ce este programat să facă ((puteți să-l înlocuiți). În general, implicit - da, vor apărea subiecte noi lentă în REASON.

0
adăugat
Parallel.ForEach() într-o colecție de elemente n nu va crea în general n Task s prea ineficientă. El împarte colecția de surse și creează numai ca Task s ca TaskScheduler care îi permite să ruleze.
adăugat autor svick, sursa