Multithreading Design Best Practice

Luați în considerare această problemă: am un program care ar trebui să preia (să zicem) 100 de înregistrări dintr-o bază de date și apoi pentru fiecare să primească informații actualizate de la un serviciu web. Există două moduri de introducere a paralelismului în acest scenariu:

  1. Începe fiecare cerere către serviciul web pe un nou subiect. Numărul de fire simultane este controlat de un anumit parametru extern (sau ajustat dinamic într-un fel).

  2. Creez loturi mai mici (să zicem câte 10 înregistrări fiecare) și să lansăm fiecare lot pe un fir separat (deci luând exemplul nostru, 10 fire).

Care este o abordare mai bună și de ce crezi asta?

0
fr hi bn
@ Patrick Ei bine, mă gândeam la ThreadPool în ceea ce privește controlul dinamic. Dar cred ca incerc sa inteleg daca exista o performanta diferita intre cele doua abordari (ThreadPool ar putea fi folosit in ambele, de fapt). Și dacă nu performanță, există o bună practică pe care ar trebui să o urmezi.
adăugat autor Vaibhav, sursa
Acest lucru pare a fi un loc de muncă pentru un ThreadPool . Așezați-vă singur joburile și lăsați .net să se ocupe de restul.
adăugat autor Patrick, sursa
Dinamic/configurabil, deoarece numărul optim depinde în totalitate de mediul înconjurător și de ceea ce este de fapt gâtuirea.
adăugat autor Stu, sursa

4 răspunsuri

Opțiunea 3 este cea mai bună:

Utilizați Async IO.

Cu excepția cazului în care procesarea cererilor dvs. este complexă și greoaie, programul dvs. va cheltui 99% din timpul în care așteaptă cererile HTTP.

Acesta este exact ceea ce Async IO este proiectat pentru - Lăsați stack-ul de rețea Windows (sau .net framework sau orice altceva) să vă faceți griji în legătură cu toate așteptările și să folosiți doar un singur fir pentru a expedia și a "ridica" rezultatele.

Din păcate, cadrul .NET îl face o adevărată durere în fund. Este mai ușor dacă folosiți numai prize de tip raw sau Win32 api. Iată un exemplar (testat!) Folosind C# 3 oricum:

using System.Net;//need this somewhere

// need to declare an class so we can cast our state object back out
class RequestState {
    public WebRequest Request { get; set; }
}

static void Main( string[] args ) {
   //stupid cast neccessary to create the request
    HttpWebRequest request = WebRequest.Create( "http://www.stackoverflow.com" ) as HttpWebRequest;

    request.BeginGetResponse(
        /* callback to be invoked when finished */
        (asyncResult) => { 
           //fetch the request object out of the AsyncState
            var state = (RequestState)asyncResult.AsyncState; 
            var webResponse = state.Request.EndGetResponse( asyncResult ) as HttpWebResponse;

           //there we go;
            Debug.Assert( webResponse.StatusCode == HttpStatusCode.OK ); 

            Console.WriteLine( "Got Response from server:" + webResponse.Server );
        },
        /* pass the request through to our callback */
        new RequestState { Request = request }  
    );

   //blah
    Console.WriteLine( "Waiting for response. Press a key to quit" );
    Console.ReadKey();
}

EDITAȚI | ×:

În cazul .NET, "apelul de completare" este de fapt concediat într-un thread ThreadPool, nu în firul dvs. principal, deci va trebui să blocați oricare dintre resursele partajate, dar tot vă salvează toate problemele legate de gestionarea firelor.

0
adăugat
Chiar trebuie să transmiteți cererea utilizând obiectul de stare sau puteți utiliza solicitarea ca variabilă de închidere?
adăugat autor zvikara, sursa

Obțineți Parallel Fx . Uită-te la BlockingCollection. Utilizați un fir pentru al alimenta loturi de înregistrări și 1 până la n fileuri care trag înregistrări din colecție pentru a le deservi. Puteți controla rata la care este alimentată colecția și numărul de fire care se conectează la serviciile web. Faceți-o configurabilă printr-o ConfigSection și faceți-o generică prin alimentația delegaților Acțiune de colectare și veți avea un dozator frumos, pe care îl puteți reutiliza în conținutul inimii.

0
adăugat

Calculatorul care rulează programul nu este, probabil, blocajele, astfel: Amintiți-vă că protocolul HTTP are un antet care vă permite să trimiteți mai multe solicitări GET pe aceleași prize, ceea ce vă economisește din agitația mâinilor TCP/IP. Din păcate, nu știu cum să o folosesc în bibliotecile .net. (Ar trebui să fie posibil.)

Probabil va exista, de asemenea, o întârziere în răspunsul la cererile dumneavoastră. Puteți încerca să vă asigurați că aveți întotdeauna un număr dat de cereri restante la server.

0
adăugat

Două lucruri de luat în considerare.

1. Cât timp va dura pentru a procesa o înregistrare?

În cazul în care procesarea înregistrărilor este foarte rapidă, operațiunile de înmânare a înregistrărilor la fire pot deveni o strangulare. În acest caz, ați dori să legați înregistrările astfel încât să nu trebuiască să le înmânați atât de des.

Dacă procesarea înregistrărilor este în mod rezonabil lungă, diferența va fi neglijabilă, astfel că abordarea mai simplă (1 înregistrare pe fir) este probabil cea mai bună.

2. Câte fire aveți de gând să începeți?

Dacă nu utilizați un filet de filet, cred că fie trebuie să limitați manual numărul de fire, fie că trebuie să rupeți datele în bucăți mari. Pornirea unui nou fir pentru fiecare înregistrare va lăsa sistemul dvs. să tremure dacă numărul de înregistrări crește.

0
adăugat
Da, aceste considerente sunt utile. Deoarece acest lucru se numește webservice de domeniu public, cred că ar putea să încercăm să facem niște teste pentru a vedea dacă cheltuielile aeriene depășesc locul de muncă (mă îndoiesc). Și da, utilizarea ThreadPool este ceva pe care l-am fi considerat cu siguranță.
adăugat autor Vaibhav, sursa