folosind pthreads ca un pool de fire cu coada?

Sunt foarte nou la C, așa că îmi pare rău dacă această întrebare este foarte aproape. Am folosit serviciul de executare Java pentru a face baze fixe de fire și a avut probleme cu înțelegerea modului de a face ceva similar în C.

Am învățat cum să creez fire în c folosind pthreads (ceea ce pare destul de ușor), dar nu sunt sigur cum să creez o coadă pe care un număr fix de fire consumă? Toate tutorialele pe care le-am realizat până acum, fie încep în mod independent subiectele în declarația lor principală, fie o fac în cadrul unui buclă. Dacă fac această abordare atunci voi avea milioane de fire (1 pentru fiecare articol de lucru) când tot ce vreau este de 3 sau 4 și să le proceseze o coadă.

Este posibil acest lucru și, dacă da, de ce trebuie să învăț? Dacă nu este posibil cu pthreads, atunci sunt bucuros să folosesc altceva, eu sunt în curs de dezvoltare pe un Mac și de gând să-l implementa pe linux.

0
@JensGustedt procesez un fișier log 100GB și fiecare linie are nevoie de aproximativ 5 milioane de operațiuni pe el. Cred că datele vor veni mai repede decât o pot procesa, așa că am vrut să am o coadă de blocare și să am firele care o consumă. B/C volumul de lucru este atât de greu am crezut că ar fi mai bine pentru a controla numărul de fire (nu face acest lucru pentru crearea/ștergerea firului de fir eficient, dar B/C partea mea de prelucrare este foarte CPU intensiv). Fac multe ipoteze aici (văzând lucruri pe care le-am făcut în programele mele Java), așa că vă rugăm să corectați presupune
adăugat autor Error_404, sursa
@JensGustedt Înțeleg că voi urma sfatul dvs. în ceea ce privește construirea și testarea (am deja cod de lucru, dar nu este filetat). Încerc să fac acest lucru nu atât de mult pentru a optimiza aspectul de filetare, ci ca o modalitate de control al fluxului folosind o coadă de blocare și având firele procesează-o. Eu citesc despre cum să fac o coadă de blocare, dar nu-mi dau seama cum să păstrez thread-urile în viață pentru a procesa coada de așteptare (pot încerca să le aibă într-o buclă infinită dar nu sunt sigur dacă este o idee bună). Doar pentru a-mi clarifica intențiile. Cu toate acestea
adăugat autor Error_404, sursa
@JensGustedt Multumesc mult. Voi încerca tot ce pot să învăț. Vă mulțumim mult pentru răbdarea și sfaturile dvs. până acum, a fost foarte util.
adăugat autor Error_404, sursa
aveți un motiv special să doriți acest lucru? crearea și ștergerea firelor este destul de eficientă în zilele noastre. Dacă nu aveți o aplicație în care acest lucru este critic (multe lucruri nu fac aproape nimic), trebuie să vă concentrați asupra altor lucruri (corectitudine, viață, curse ...)
adăugat autor Jens Gustedt, sursa
Descrierea problemei dvs. pare a fi destul de complicată încât este deja o provocare destul de mare dacă nu sunteți încă prea experimentat pentru a obține lucrurile corecte în C. Încercați să faceți lucrurile mai bine, mai întâi. Atunci, dacă descoperiți că perfomanța nu este ceea ce așteptați, măsurați. Acesta este singurul mod de a ști. Foarte des, chiar și programatorii cu experiență nu estimează corect unde sunt localizate blocajele programelor lor. Deschiderea fileului poate fi uneori un obstacol, dar cu siguranță nu este primul lucru pe care trebuie să-l căutați.
adăugat autor Jens Gustedt, sursa
O abordare standard ar fi folosirea unui model de consum pentru producători. Acest lucru poate fi realizat prin folosirea unei mutex și a unei variabile a condiției pentru a face semnalizarea între fire.
adăugat autor Jens Gustedt, sursa

1 răspunsuri

Puteți face acest lucru cu un model uniproducer/multiconfurent în mod rezonabil ușor prin utilizarea variabilelor de condiție. Rețineți că aceasta este o arhitectură, altele sunt cu siguranță posibile.

În fișierul principal, creați pur și simplu coada, mutexul și variabila condiție, apoi porniți cât mai multe fire pe care doriți să le executați, pseudo-cod, cum ar fi:

glbQueue = []
glbMutex = new mutex
glbCondVar = new condvar
for i = 1 to 10:
    start thread using thrdFn

Pasul următor este adăugarea oricăror workitems de care aveți nevoie la coadă (folosind mutexul) și lovirea variabilei condiție pentru a trezi firele după cum este necesar:

while workitem = getNextWorkItem():
    lock glbMutex
    glbQueue.append (workItem)
    kick glbCondVar
    unlock glbMutex

Odată ce toate elementele de lucru sunt terminate, așteptați ca coada să se goale, apoi postați elemente de tip santinel pentru a închide firele și așteptați ca acestea să se termine înainte de a ieși.

lock glbMutex
while glbQueue is not empty:
    kick glbCondVar
    unlock glbMutex.
    sleep for a bit
    lock glbMutex
unlock glbMutex.

for i = 1 to 10:
    lock glbMutex
    glbQueue.append (endWorkItem)
    kick glbCondVar
    unlock glbMutex.
    wait for any one thread to exit
exit

Firele care fac lucrarea sunt de asemenea relativ simple. În primul rând, ele rulează într-o buclă infinită așteptând ca variabila condiției să fie lovită. În cadrul acelei buclă procesează articole de lucru până când nu mai sunt disponibile, apoi se întorc la culcare.

Odată ce elementul de lucru final a fost primit de un fir, acesta iese, garantând că fiecare fir primește un singur element final.

Cu alte cuvinte, ceva de genul:

initialise
stillGoing = true
lock glbMutex
while stillGoing:
    wait on glbCondVar using glbMutex
    while stillGoing and glbQueue is not empty:
        extract workItem from glbQueue to thread local storage
        unlock glbMutex.
        if workItem is endWorkItem:
            stillGoing = false
        else:
            do the work specified by workItem
        lock glbMutex
unlock glbMutex
clean up
exit thread

Acest lucru vă permite în mod substanțial să aveți un număr fix de fire de procesare a elementelor de pe coadă și coada însăși este protejată de mutex, astfel încât să nu existe nici o dispută între firele lucrătorului sau firul principal.

0
adăugat