Gentoo Archives: gentoo-commits

From: "Davide Cendron (scen)" <scen@g.o>
To: gentoo-commits@l.g.o
Subject: [gentoo-commits] gentoo commit in xml/htdocs/doc/it/articles: l-posix3.xml
Date: Thu, 01 May 2008 15:12:07
Message-Id: E1JraS3-0003A1-5k@stork.gentoo.org
1 scen 08/05/01 15:12:03
2
3 Added: l-posix3.xml
4 Log:
5 Initial commit: version 1.4, revision 1.6 of EN CVS
6
7 Revision Changes Path
8 1.1 xml/htdocs/doc/it/articles/l-posix3.xml
9
10 file : http://sources.gentoo.org/viewcvs.py/gentoo/xml/htdocs/doc/it/articles/l-posix3.xml?rev=1.1&view=markup
11 plain: http://sources.gentoo.org/viewcvs.py/gentoo/xml/htdocs/doc/it/articles/l-posix3.xml?rev=1.1&content-type=text/plain
12
13 Index: l-posix3.xml
14 ===================================================================
15 <?xml version='1.0' encoding="UTF-8"?>
16 <!-- $Header: /var/cvsroot/gentoo/xml/htdocs/doc/it/articles/l-posix3.xml,v 1.1 2008/05/01 15:12:02 scen Exp $ -->
17 <!DOCTYPE guide SYSTEM "/dtd/guide.dtd">
18
19 <guide link="/doc/it/articles/l-posix3.xml" disclaimer="articles" lang="it">
20 <title>Spiegazioni sui thread POSIX, parte 3</title>
21
22 <author title="Autore">
23 <mail link="drobbins@g.o">Daniel Robbins</mail>
24 </author>
25 <author title="Traduzione">
26 <mail link="zanetti.massimo@×××××.com">Massimo Zanetti</mail>
27 </author>
28
29 <abstract>
30 In questo articolo, l'ultimo di una serie di tre sui thread POSIX, Daniel dà
31 una buona idea su come usare le variabili di condizione. Le variabili di
32 condizione sono strutture di thread di POSIX che vi permettono di "risvegliare"
33 i thread al verificarsi di certe condizioni. Potete pensare a loro come di una
34 forma di signalling thread sicura. Daniel riempie l'articolo usando tutto
35 quello che avete imparato fino ad adesso per sviluppare applicazioni work crew
36 multi-thread.
37 </abstract>
38
39
40 <!-- La versione originale di questo articolo è stata pubblicata su
41 IBM developerWorks, ed è di proprietà  della Westtech Information
42 Services.Questo documento è una versione aggiornata dell'articolo
43 originale, e contiene diversi miglioramenti fatti dal Gentoo Linux
44 Documentation Team
45 -->
46
47 <version>1.4</version>
48 <date>2005-10-09</date>
49
50 <chapter>
51 <title>Migliorare l'efficenza con le variabili di condizione</title>
52 <section>
53 <title>Le variabili di condizione spiegate</title>
54 <body>
55
56 <p>
57 Ho finito il mio <uri link="/doc/it/articles/l-posix2.xml">articolo
58 precedente</uri> descrivendo un particolare dilemma su come faccia un thread a
59 gestire una situazione in cui sta aspettando che una determinata condizione
60 diventi vera. Potrebbe ripetutamente bloccare /sbloccare un mutex, controllando
61 ogni volta per un certo valore una struttura dati condivisa. Ma questa è una
62 perdita di tempo e di risorse e questa forma di busy polling è estremamente
63 inefficiente. Il miglior modo per farlo è usare la chiamata pthread_cond_wait()
64 per attendere che una determinata condizione diventi vera.
65 </p>
66
67 <p>
68 E' importante capire che cosa pthread_cond_wait() faccia -- è il cuore del
69 sistema di segnalazione dei thread di POSIX ed è anche la parte più difficile
70 da capire.
71 </p>
72
73 <p>
74 Per prima cosa consideriamo una scenario in cui un thread ha bloccato un
75 mutex per leggere una lista linkata e la lista è vuota. Questo particolare
76 thread non può fare nulla -- è scritto per togliere un nodo dalla lista e
77 non ce ne sono disponibili. Così ecco cosa fa.
78 </p>
79
80 <p>
81 Mentre continua a tenere il mutex bloccato, il nostro thread chiama
82 pthread_cond_wait(&amp;mycond,&amp;mymutex) la chiamata a pthread_cond_wait() è
83 abbastanza complessa così che affrontiamo ciascuna operazione un passo
84 alla volta.
85 </p>
86
87 <p>
88 La prima cosa che p_thread _cond_wait() fa è bloccare il mutex mymutex (così
89 che gli altri thread possono modificare la lista linkata) e contemporaneamente
90 aspetta la condizione mycond (così che pthread_cond_wait() si sveglia quando
91 riceve un segnale da un altro thread). Ora che il mutex è sbloccato, altri
92 thread possono accedere e modificare la lista linkata, possibilmente aggiungendo
93 altri oggetti.
94
95 </p>
96
97 <p>
98 A questo punto la chiamata pthread_cond_wait() non è ancora ritornata.
99 Lo sbloccaggio del mutex avviene immediatamente, ma aspettare per la condizione
100 mycond è normalmente un'operazione che blocca, ciò significa che il nostro
101 thread va in sleep, senza consumare alcun ciclo di CPU fine al momento del
102 risveglio. Questo è esattamente quello che vogliano che succeda. Il nostro
103 thread è in sleep, aspettando che una determinata condizione diventi vera,
104 senza fare nessun tipo di "busy polling" che sprecherebbe tempo di CPU. Dal
105 punto di vista del nostro thread, sta semplicemente aspettando che ritorni la
106 chiamata pthread_cond_wait().
107 </p>
108
109 <p>
110 Ora, per continuare con la spiegazione, diciamo che un altro thread
111 (chiamiamolo "thread 2") blocchi mymutex e aggiunga un oggetto alla nostra
112 lista linkata. Immediatamente dopo aver sbloccato il mutex, il thread 2 chiama
113 la funzione pthread_cond_broadcast(&amp;mycond). Facendo questo, il thread 2 fa
114 svegliare immediatamente tutti quei thread che aspettavano la variabile di
115 condizione mycond. Questo significa che il nostro primo thread (che è nel mezzo
116 di una chiamata pthread_cond_wait()) adesso si sveglia.
117 </p>
118
119 <p>
120 Ora diamo un'occhiata a cosa succede al nostro primo thread. Dopo che il thread
121 2 ha chiamato pthread_cond_broadcast(&amp;mymutex) potreste pensare che la
122 pthread_cond_wait() del thread 1 ritorni immediatamente. Non è così! Invece,
123 pthread_cond_wait() eseguirà un'ultima operazione: ribloccare mymutex. Una
124 volta che pthread_cond_wait() ha il blocco, allora ritornerà e permetterà a
125 thread 1 di continuare l'esecuzione. A quel punto, può immediatamente
126 controllare la lista per qualsiasi cambiamento degno di nota.
127 </p>
128
129 </body>
130 </section>
131 <section>
132 <title>Fermati e riguarda!</title>
133 <body>
134
135 <!-- These bits do not make any sense to me, commented out
136
137 <pre caption="queue.h">
138 pthread_cond_t mycond;
139 </pre>
140
141 <pre caption="control.h">
142 pthread_cond_t mycond;
143
144 pthread_cond_init(&amp;mycond,NULL);
145
146 pthread_cond_destroy(&amp;mycond);
147
148 pthread_cond_wait(&amp;mycond, &amp;mymutex);
149
150 pthread_cond_broadcast(&amp;mycond);
151
152 pthread_cond_signal(&amp;mycond);
153 </pre>
154 -->
155 <pre caption="queue.h">
156 /* queue.h
157 <comment>** Copyright 2000 Daniel Robbins, Gentoo Technologies, Inc.
158 ** Autore: Daniel Robbins
159 ** Data: 16 Giugno 2000</comment>
160 */
161 typedef struct node {
162 struct node *next;
163 } node;
164 typedef struct queue {
165 node *head, *tail;
166 } queue;
167 void queue_init(queue *myroot);
168 void queue_put(queue *myroot, node *mynode);
169 node *queue_get(queue *myroot);
170 </pre>
171
172 <pre caption="queue.c">
173 /* queue.c
174 <comment>** Copyright 2000 Daniel Robbins, Gentoo Technologies, Inc.
175 ** Autore: Daniel Robbins
176 ** Data: 16 Giugno 2000
177 **
178 ** Questo insieme di funzioni di coda era originariamente conscio dei thread.
179 ** Io l'ho scritto per fare in modo che questo insieme di routine di coda sia
180 ** thread-ignorante (giusto un generico noioso ma molto veloce insieme di
181 ** routine di coda). Perché questo cambiamento? Perché ha più senso aver il
182 ** supporto ai thread come un add-on opzionale. Si consideri una situazione in
183 ** cui si vogliono aggiungere 5 nodi alla coda. Con la versione con i thread,
184 ** ciascuna chiamata a queue_port() automaticamente bloccherebbe o sbloccherebbe
185 ** il mutex della coda 5 volte-- c'è molto overhead non necessario.
186 ** Tuttavia, muovendo la parte relativa al thread al di fuori delle routine di
187 ** coda, il chiamante può bloccare il mutex una volta all'inizio, dopo di che
188 ** inserisce i cinque oggetti, ed infine lo sblocca. Spostare il codice di
189 ** blocco/sblocco al di fuori delle funzioni di coda permette ottimizzazioni
190 ** altrimenti impossibili. Rende inoltre questo codice utile ad applicazioni
191 ** senza thread.
192 **
193 ** Possiamo facilmente rendere thread-enable questa struttura dati usando il
194 ** tipo data-control definito in control.c e control.h.</comment> */
195 #include &lt;stdio.h&gt;
196 #include "queue.h"
197 void queue_init(queue *myroot) {
198 myroot->head=NULL;
199 myroot->tail=NULL;
200 }
201 void queue_put(queue *myroot,node *mynode) {
202 mynode->next=NULL;
203 if (myroot->tail!=NULL)
204 myroot->tail->next=mynode;
205 myroot->tail=mynode;
206 if (myroot->head==NULL)
207 myroot->head=mynode;
208 }
209 node *queue_get(queue *myroot) {
210 //get from root
211 node *mynode;
212 mynode=myroot->head;
213 if (myroot->head!=NULL)
214 myroot->head=myroot->head->next;
215 return mynode;
216 }
217 </pre>
218
219 <pre caption="control.h">
220 #include &lt;pthread.h&gt;
221 typedef struct data_control {
222 pthread_mutex_t mutex;
223 pthread_cond_t cond;
224 int active;
225 } data_control;
226 </pre>
227
228 <pre caption="control.c">
229 /* control.c
230 <comment>** Copyright 2000 Daniel Robbins, Gentoo Technologies, Inc.
231 ** Autore: Daniel Robbins
232 ** Data: 16 Giugno 2000
233 **
234 ** Queste routine forniscono un modo semplice per rendere qualsiasi tipo di
235 ** strutture dati thread-aware. Semplicemente associamo una struttura
236 ** data-control con la struttura dati (creando una nuova struttura, ad
237 ** esempio). Dopo, semplicemente, bloccano e sbloccano il mutex, o
238 ** (aspetta/segnala/trasmette) la variabile di condizione nella struttura
239 ** data_control come necessario.
240 **
241 ** Le strutture data_control contengono un int chiamato "active". Lo scopo di
242 ** questo int è di essere usato per uno specifico tipo di progetto
243 ** multi-threaded, dove ciascun thread controlla lo stato dell'"active" ogni
244 ** volta che blocca il mutex. Se "active" è 0 il thread sa che invece di fare
245 ** la sua normale routine deve a sua volta fermarsi.
246 ** Se active è 1, dovrebbe continuare come sempre. Così, impostando active a
247 ** 0, un thread di controllo può facilmente informare un thread work crew di
248 ** spegnersi invece che processare nuovi job. Si usino le funzione
249 ** control_activate() e control_deactivate(), che trasmetteranno anche sulla
250 ** variabile di condizione della struttura data_control, così che tutti i
251 ** thread fermi in pthread_cond_wait() si sveglino, abbiano modo di notare il
252 ** cambiamento ed infine terminino.</comment>*/
253 #include "control.h"
254 int control_init(data_control *mycontrol) {
255 int mystatus;
256 if (pthread_mutex_init(&amp;(mycontrol->mutex),NULL))
257 return 1;
258 if (pthread_cond_init(&amp;(mycontrol->cond),NULL))
259 return 1;
260 mycontrol->active=0;
261 return 0;
262 }
263 int control_destroy(data_control *mycontrol) {
264 int mystatus;
265 if (pthread_cond_destroy(&amp;(mycontrol->cond)))
266 return 1;
267 if (pthread_mutex_destroy(&amp;(mycontrol->cond)))
268 return 1;
269 mycontrol->active=0;
270 return 0;
271 }
272 int control_activate(data_control *mycontrol) {
273 int mystatus;
274 if (pthread_mutex_lock(&amp;(mycontrol->mutex)))
275 return 0;
276 mycontrol->active=1;
277 pthread_mutex_unlock(&amp;(mycontrol->mutex));
278 pthread_cond_broadcast(&amp;(mycontrol->cond));
279 return 1;
280 }
281 int control_deactivate(data_control *mycontrol) {
282 int mystatus;
283 if (pthread_mutex_lock(&amp;(mycontrol->mutex)))
284 return 0;
285 mycontrol->active=0;
286 pthread_mutex_unlock(&amp;(mycontrol->mutex));
287 pthread_cond_broadcast(&amp;(mycontrol->cond));
288 return 1;
289 }
290 </pre>
291
292 </body>
293 </section>
294 <section>
295 <title>Debug time</title>
296 <body>
297
298 <p>
299 Un altro file misto prima di arrivare a quello grosso. Ecco <path>dbug.h</path>:
300 </p>
301
302 <pre caption="dbug.h">
303 #define dabort() \
304 { printf("Aborting at line %d in source file %s\n",__LINE__,__FILE__);
305 abort(); }
306 </pre>
307
308 <p>
309 Usiamo questo codice per gestire errori irrecuperabili nel nostro codice work
310 crew.
311 </p>
312
313 </body>
314 </section>
315 <section>
316 <title>Il codice di work crew</title>
317 <body>
318
319 <p>
320 Parlando del codice di work crew code, eccolo:
321 </p>
322
323 <pre caption="workcrew.c&gt;">
324 #include &lt;stdio.h&gt;
325 #include &lt;stdlib.h&gt;
326 #include "control.h"
327 #include "queue.h"
328 #include "dbug.h"
329 /* <comment>Il work_queue tiene i task per i differenti thread da
330 completare.</comment>*/
331 struct work_queue {
332 data_control control;
333 queue work;
334 } wq;
335 /* <comment>Ho aggiunto un numero di job al work node. Normalmente il work node
336 contiene ulteriori dati che hanno bisogno di essere processati.</comment>*/
337 typedef struct work_node {
338 struct node *next;
339 int jobnum;
340 } wnode;
341 /* <comment>La coda di cleanup conserva i thread formati. Prima che un threa
342 d termini aggiunge se stesso a questa lista. Siccome il thread principale
343 sta aspettando le modifiche di questa lista, si sveglia e "pulisce" il
344 thread appena terminato. </comment>*/
345 struct cleanup_queue {
346 data_control control;
347 queue cleanup;
348 } cq;
349 /* <comment>Ho aggiunto un numero al thread (ad uso debugging/studio) e una
350 thread id al nodo cleanup. Il nodo cleanup viene passato al nuovo thread
351 nell'avvio, e un attimo prima che il thread si fermi, attacca il nodo cleanup
352 alla coda di cleanup. Il thread principale monitora la coda di cleanup ed è
353 quello che esegue il necessario cleanup.</comment> */
354 typedef struct cleanup_node {
355 struct node *next;
356 int threadnum;
357 pthread_t tid;
358 } cnode;
359 void *threadfunc(void *myarg) {
360 wnode *mywork;
361 cnode *mynode;
362 mynode=(cnode *) myarg;
363 pthread_mutex_lock(&amp;wq.control.mutex);
364 while (wq.control.active) {
365 while (wq.work.head==NULL &amp;&amp; wq.control.active) {
366 pthread_cond_wait(&amp;wq.control.cond, &amp;wq.control.mutex);
367 }
368 if (!wq.control.active)
369 break;
370 //we got something!
371 mywork=(wnode *) queue_get(&amp;wq.work);
372 pthread_mutex_unlock(&amp;wq.control.mutex);
373 //perform processing...
374 printf("Thread number %d processing job
375 %d\n",mynode->threadnum,mywork->jobnum);
376 free(mywork);
377 pthread_mutex_lock(&amp;wq.control.mutex);
378 }
379 pthread_mutex_unlock(&amp;wq.control.mutex);
380 pthread_mutex_lock(&amp;cq.control.mutex);
381 queue_put(&amp;cq.cleanup,(node *) mynode);
382 pthread_mutex_unlock(&amp;cq.control.mutex);
383 pthread_cond_signal(&amp;cq.control.cond);
384 printf("thread %d shutting down...\n",mynode-&gt;threadnum);
385 return NULL;
386
387 }
388 #define NUM_WORKERS 4
389 int numthreads;
390 void join_threads(void) {
391 cnode *curnode;
392 printf("joining threads...\n");
393 while (numthreads) {
394 pthread_mutex_lock(&amp;cq.control.mutex);
395 /* <comment>sotto, dormiamo fino a che c'è veramente un nodo cleanup.
396 Questo si prende cura di ogni falso risveglio ... Anche se usciamo da
397 pthread_cond_wait(), non riteniamo che la condizione che stiamo
398 aspettando sia
399 vera.</comment>*/
400 while (cq.cleanup.head==NULL) {
401 pthread_cond_wait(&amp;cq.control.cond,&amp;cq.control.mutex);
402 }
403 /* <comment>a questo punto, conserviamo il mutex e c'è un item nella lista
404 che dobbiamo processare. Per prima cosa rimuoviamo il nodo dalla
405 coda. Poi, chiamiamo pthread_join() sulla parte immagazzinata nel node. Quando
406 pthread_join() ritorna, è tutto pulito dopo il thread. Solo allora facciamo
407 un free() al node, decrementiamo il numero di thread addizionali
408 che dobbiamo aspettare e ripetiamo l'intero processo, se necessario</comment> */
409 curnode = (cnode *) queue_get(&amp;cq.cleanup);
410 pthread_mutex_unlock(&amp;cq.control.mutex);
411 pthread_join(curnode->tid,NULL);
412 printf("joined with thread %d\n",curnode->threadnum);
413 free(curnode);
414 numthreads--;
415 }
416 }
417 int create_threads(void) {
418 int x;
419 cnode *curnode;
420 for (x=0; x&lt;NUM_WORKERS; x++) {
421 curnode=malloc(sizeof(cnode));
422 if (!curnode)
423 return 1;
424 curnode->threadnum=x;
425 if (pthread_create(&amp;curnode->tid, NULL, threadfunc, (void *) curnode))
426 return 1;
427 printf("created thread %d\n",x);
428 numthreads++;
429 }
430 return 0;
431 }
432 void initialize_structs(void) {
433 numthreads=0;
434 if (control_init(&amp;wq.control))
435 dabort();
436 queue_init(&amp;wq.work);
437 if (control_init(&amp;cq.control)) {
438 control_destroy(&amp;wq.control);
439 dabort();
440 }
441 queue_init(&amp;wq.work);
442 control_activate(&amp;wq.control);
443 }
444 void cleanup_structs(void) {
445 control_destroy(&amp;cq.control);
446 control_destroy(&amp;wq.control);
447 }
448 int main(void) {
449 int x;
450 wnode *mywork;
451 initialize_structs();
452 /* CREATION */
453
454 if (create_threads()) {
455 printf("Error starting threads... cleaning up.\n");
456 join_threads();
457 dabort();
458 }
459 pthread_mutex_lock(&amp;wq.control.mutex);
460 for (x=0; x&lt;16000; x++) {
461 mywork=malloc(sizeof(wnode));
462 if (!mywork) {
463 printf("ouch! can't malloc!\n");
464 break;
465 }
466 mywork->jobnum=x;
467 queue_put(&amp;wq.work,(node *) mywork);
468 }
469 pthread_mutex_unlock(&amp;wq.control.mutex);
470 pthread_cond_broadcast(&amp;wq.control.cond);
471 printf("sleeping...\n");
472 sleep(2);
473 printf("deactivating work queue...\n");
474 control_deactivate(&amp;wq.control);
475 /* CLEANUP */
476 join_threads();
477 cleanup_structs();
478 }
479 </pre>
480
481 </body>
482 </section>
483 <section>
484 <title>Attraverso il codice</title>
485 <body>
486
487 <p>
488 Adesso è ora di fare una veloce passeggiata attraverso il codice. La prima
489 struttura definita è chiamata "wq", e contiene un data_control e una queue
490 header. La struttura data_control viene usata per arbitrare l'accesso
491 all'intera coda, inclusi i nodi. Il nostro prossimo lavoro è definire
492 i reali nodi di lavoro. Per mantenere il codice snello in maniera da
493 farlo entrare in questo articolo tutto quello che c'è qui è un job number.
494 </p>
495
496 <p>
497 Successivamente creiamo una coda per cleanup. I commenti mostrano come ciò
498 funzioni. OK, per ora saltiamo le chiamate a threadfunc(), join_threads(),
499 create_threads() e initialize_structs(), e saltiamo alla main(). La prima
500 cosa che facciamo è inizializzare le nostre strutture -- questo include
501 l'inizializzare la nostra data_controls e le code, come anche attivare la
502 nostra coda di lavoro.
503 </p>
504
505 </body>
506 </section>
507 <section>
508 <title>Cleanup special</title>
509 <body>
510
511 <p>
512 Ora è il momento di inizializzare i nostri thread. Se si guarda alla chiamata
513 alla nostra create_threads(), tutto sembra piuttosto normale...eccetto una cosa.
514 Si noti che noi allochiamo un nodo cleanup, inizializziamo il suo numero di
515 thread e componenti TID. Passiamo inoltre un nodo cleanup a ciascun nuovo worker
516 thread come argomento iniziale.perché lo facciamo?
517 </p>
518
519 <p>
520 Perché quando un worker thread esce, attaccherà il suo nodo cleanup alla coda
521 cleanup e terminerà. In seguito il nostro thread principale noterà questa
522 aggiunta alla coda cleanup (grazie all'uso di una variabile di condizione) e
523 dequeue il nodo. Siccome la TID (id del thread) è salvata nel nodo cleanup, il
524 nostro thread principale saprà esattamente quale thread terminare. Allora il
525 nostro thread principale chiamerà pthread_join(tid), e con il worker si
526 attaccherà al thread appropriato. Se non facessimo questo tipo di controllo, il
527 nostro thread principale si dovrebbe attaccare ai worker thread in modo
528 arbitrario. Presumibilmente nell'ordine in cui sono stati creati. Siccome i
529 thread non devono necessariamente terminare in quest'ordine, il nostro thread
530 principale potrebbe stare aspettando di unirsi con un thread mentre avrebbe
531 potuto unirsi con altri dieci. Riuscite a vedere come questa scelta di
532 progetto possa realmente velocizzare il nostro codice di spegnimento
533 specialmente se si usano centinaia di worker thread?
534 </p>
535
536 </body>
537 </section>
538 <section>
539 <title>Creare lavoro</title>
540 <body>
541
542 <p>
543 Ora che abbiamo fatto partire i nostri worker thread (e che stanno facendo
544 andare le loro threadfunc(), di cui parleremo tra poco), il nostro thread
545 principale incomincia ad inserire oggetti all'interno della coda di work. Per
546 prima cosa, blocca il controllo mutex di wq, e poi alloca 16000 pacchetti work
547 inserendoli ad uno ad uno all'interno della coda. Dopo di che,
548 pthread_cond_broadcast() è chiamata così che, qualsiasi thread dormiente viene
549 svegliato è puo fare il lavoro. Allora il nostro thread principale dorme per
550 due secondi, dopo di che disattiva la work queue dicendo ai worker thread di
551 terminare. Quindi il nostro thread principale chiama le funzioni join_threads()
552 per pulire tutti i worker thread.
553 </p>
554
555 </body>
556 </section>
557 <section>
558 <title>threadfunc()</title>
559 <body>
560
561 <p>
562 E' ora di guardare threadfunc(), il codice che ciascun work thread esegue.
563 Quando un worker thread inizia, immediatamente blocca il mutex della work queue,
564 prende un node work (se disponibile) e lo processa. Se non c'è un work
565 disponibile pthread_cond_wait() viene chiamato. Noterete che è chiamato
566 in un ciclo while() molto stretto e questo è molto importante. Quando ci
567 si sveglia da una chiamata pthread_cond_wait(), non si dovrebbe mai
568 presupporre che la nostra condizione sia assolutamente vera, probabilmente lo
569 sarà ma potrebbe anche non esserlo. Il ciclo while() forza pthread_cond_wait()
570 ad essere richiamato se accadesse che il thread venisse erroneamente svegliato
571 e la lista fosse vuota.
572 </p>
573
574 <p>
575 Se c'è un work node, semplicemente stampiamo il suo numero di job, lo liberiamo
576 e usciamo. Nella realtà il codice farebbe qualche cosa di più sostanziale. Alla
577 fine del ciclo while(), blocchiamo il mutex così che possiamo controllare la
578 variabile attiva come anche controllare nuovi work node all'inizio del ciclo. Se
579 si segue il codice si troverà che il wq.control.active è 0, il ciclo while()
580 sarà terminato e il codice di cleanup alla fine di threadfunc() ricomincerà.
581 </p>
582
583 <p>
584 La parte del worker thread è abbastanza interessante. Primo sblocca la
585 work_queue, poiché se il mutex è bloccato phread_cond_wait() ritorna. Dopo di
586 che prende un lock sulla code cleanup, aggiunge il nostro cleanup node (che
587 contiene la nostra TID, che il thread principale userà per la sua chiamata a
588 pthread_join(), e dopo sbloccherà la cleanup queue. Dopo di che segnala a cq
589 waiters (pthread_cond_signal(&amp;cq.control.cond)) così che il thread
590 principale sa che c'è un nuovo nodo da processare. Non usiamo
591 pthread_cond_broadcast() perché non è necessario -- solamente un thread (il
592 thread principale) sta aspettando nuove entry nella coda di cleanup. Il nostro
593 worker thread stampa un messaggio di spegnimento e poi termina aspettando di
594 essere pthread_joined() dal thread principale quando chiama join_threads().
595 </p>
596
597 </body>
598 </section>
599 <section>
600 <title>join_threads()</title>
601 <body>
602
603 <p>
604 Se volete vedere un semplice esempio di come le variabili di condizione
605 dovrebbero essere usate, data un'occhiata alla funzione join_threads(). Mentre
606 abbiamo ancora worker thread in esistenza, join_threads() cicla,
607 aspettando nuovi nodi cleanup nella nostra coda cleanup. Se c'è un nuovo
608 nodo, si dequeue il nodo, sblocca la cleanup queue (così che altri nodi
609 di cleanup possano essere aggiungi dai nostri worker thread), si unisce con
610 il nuovo thread (usando la TID memorizzata nel nodo cleanup), libera il nodo
611 cleanup, decrementa il numero di thread "li fuori" e continua.
612 </p>
613
614 </body>
615 </section>
616 <section>
617 <title>Riassumendo</title>
618 <body>
619
620 <p>
621 Siamo arrivati alla fine della serie "Spiegazione sui thread POSIX, parte
622 tre", e spero che ora siate pronti ad aggiungere codice multithreaded alle
623 vostre applicazioni. Per maggiori informazioni vogliate guardare la sezione
624 <uri link="#resources">Resources</uri>, che contiene anche una tarball di tutti
625 i sorgenti usati in questo articolo. Alla prossima serie!
626 </p>
627
628 </body>
629 </section>
630 </chapter>
631
632 <chapter id="resources">
633 <title>Resources</title>
634 <section>
635 <body>
636
637 <ul>
638 <li>
639 E' disponibile un <uri link="/doc/en/files/l-posix-thread-3.tar.gz">tarball
640 dei sorgenti</uri> usati in questo articolo.
641 </li>
642 <li>
643 Leggete gli articoli di Daniel, Spiegazioni sui thread POSIX <uri
644 link="l-posix1.xml">Parte 1</uri> e <uri link="l-posix2.xml">Parte
645 2</uri>.
646 </li>
647 <li>
648 Date sempre un'occhiata all'amichevole pagina del manuale LINUX di pthread
649 (<c>man -k pthread</c>).
650 </li>
651 <li>
652 Per una terapia d'urto raccomando questo libro: <uri
653 link="http://search.borders.com/fcgi-bin/db2www/search/search.d2w/Details?&amp;mediaType=Book&amp;prodID=2362607">
654 Programming with POSIX Threads</uri>, di David R. Butenhof (Addison-Wesley,
655 1997). Questo è presumibilmente il miglior libro sui thread POSIX
656 disponibile.
657 </li>
658 <li>
659 I thread POSIX sono anche affrontati in questo libro: <uri
660 link="http://search.borders.com/fcgi-bin/db2www/search/search.d2w/Details?&amp;mediaType=Book&amp;prodID=2362607">
661 UNIX Network Programming - Networking APIs: Sockets and XTI</uri>, di W.
662 Richard Stevens (Prentice Hall, 1997). Questo è un classico libro, ma non
663 copre i thread così in dettaglio come invece Programming with POSIX Threads
664 fa.
665 </li>
666 <li>
667 Guardate la documentazione su: <uri
668 link="http://metalab.unc.edu/pub/Linux/docs/faqs/Threads-FAQ/html/">Linux
669 threads</uri>, di Sean Walton, KB7rfa.
670 </li>
671 <li>
672 Consultate un <uri
673 link="http://www.math.arizona.edu/swig/pthreads/threads.html">tutorial</uri>
674 sui thread POSIX di Mark Hays, Università dell'Arizona.
675 </li>
676 <li>
677 In <uri link="http://hwaci.com/sw/pttcl/pttcl.html">An Introduction to
678 Pthreads-Tcl</uri>, guardate i cambiamenti a Tcl che gli permettono di
679 essere usato con i thread POSIX.
680 </li>
681 <li>
682 Un'altro tutorial, <uri
683 link="http://dis.cs.umass.edu/~wagner/threads_html/tutorial.html">Getting
684 Started with POSIX Threads</uri>, di Tom Wagner e Don Towsley del
685 dipartimento di Computer Science presso l'Università del
686 Massachusetts,Amherst.
687 </li>
688 <li>
689 <uri link="http://moss.csc.ncsu.edu/~mueller/pthreads/">FSU PThreads</uri> è
690 una libreria C che implementa i thread POSIX per SunOS 4.1.x,Solaris 2.x,
691 SCO UNIX, FreeBSD, Linux, e DOS.
692 </li>
693 <li>
694 Fate riferimento all'home page <uri
695 link="http://www.sai.msu.su/sal/C/2/PCTHREADS.html"> per thread POSIX e DCE
696 </uri> per Linux.
697 </li>
698 <li>
699 Guardate <uri link="http://pauillac.inria.fr/~xleroy/linuxthreads/">The
700 LinuxThreads library</uri>.
701 </li>
702 <li>
703 <uri link="http://www.users.itl.net.ua/~prool/proolix.html">Proolix</uri> è
704 un semplice sistema operativo POSIX-compliant per i8086+ in continuo
705 sviluppo.
706 </li>
707 </ul>
708
709 </body>
710 </section>
711 </chapter>
712 </guide>
713
714
715
716 --
717 gentoo-commits@l.g.o mailing list