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-posix2-it.xml l-posix2.xml
Date: Thu, 01 May 2008 14:55:03
Message-Id: E1JraBX-0002zJ-SN@stork.gentoo.org
1 scen 08/05/01 14:54:59
2
3 Added: l-posix2.xml
4 Removed: l-posix2-it.xml
5 Log:
6 Renaming properly this doc
7
8 Revision Changes Path
9 1.1 xml/htdocs/doc/it/articles/l-posix2.xml
10
11 file : http://sources.gentoo.org/viewcvs.py/gentoo/xml/htdocs/doc/it/articles/l-posix2.xml?rev=1.1&view=markup
12 plain: http://sources.gentoo.org/viewcvs.py/gentoo/xml/htdocs/doc/it/articles/l-posix2.xml?rev=1.1&content-type=text/plain
13
14 Index: l-posix2.xml
15 ===================================================================
16 <?xml version='1.0' encoding="UTF-8"?>
17 <!-- $Header: /var/cvsroot/gentoo/xml/htdocs/doc/it/articles/l-posix2.xml,v 1.1 2008/05/01 14:54:59 scen Exp $ -->
18 <!DOCTYPE guide SYSTEM "/dtd/guide.dtd">
19
20 <guide link="/doc/it/articles/l-posix2.xml" disclaimer="articles" lang="it">
21 <title>Spiegazioni sui thread POSIX, parte 2</title>
22
23 <author title="Autore">
24 <mail link="drobbins@g.o">Daniel Robbins</mail>
25 </author>
26 <author title="Traduzione">
27 <mail link="zanetti.massimo@×××××.com">Massimo Zanetti</mail>
28 </author>
29
30 <abstract>
31 I thread POSIX sono un ottima modo per incrementare la reattività e le
32 prestazioni del vostro codice. In questo articolo, il secondo di una serie di
33 tre, Daniel Robbins vi mostra come proteggere l'integrità delle strutture di
34 dati condivise nel vostro codice con thread usando delle eccellenti piccole cose
35 chiamate mutex.
36 </abstract>
37
38 <!-- La versione originale di questo articolo è stata pubblicata su IBM
39 developerWorks, ed è di proprietà della Westtech Information Services. Questo
40 documento è una versione aggiornata dell'articolo originale, e contiene diversi
41 miglioramenti fatti dal Gentoo Linux Documentation Team
42 -->
43
44 <version>1.2</version>
45 <date>2005-10-09</date>
46
47 <chapter>
48 <title>Le piccole cose chiamate mutex</title>
49 <section id="thread3c">
50 <title>Mutex me!</title>
51 <body>
52
53 <p>
54 Nel mio <uri link="/doc/it/articles/l-posix1.xml">precedente articolo</uri>, ho
55 parlato del codice con thread che faceva cose inusuali ed inaspettate. Due
56 thread che incrementano una variabile globale venti volte. La variabile avrebbe
57 dovuto avere un valore finale di 40, ma invece terminava con un valore di 21.
58 Che cos'era successo? Il problema si è verificato perché un thread "cancellava"
59 ripetutamente l'incremento fatto dall'altro. Diamo un'occhiata a del codice
60 corretto che usa un <b>mutex</b> per risolvere il problema:
61 </p>
62
63 <pre caption="thread3.c">
64 #include &lt;pthread.h&gt;
65 #include &lt;stdlib.h&gt;
66 #include &lt;unistd.h&gt;
67 #include &lt;stdio.h&gt;
68
69 int myglobal;
70 pthread_mutex_t mymutex=PTHREAD_MUTEX_INITIALIZER;
71
72 void *thread_function(void *arg) {
73 int i,j;
74 for ( i=0; i&lt;20; i++ ) {
75 pthread_mutex_lock(&amp;mymutex);
76 j=myglobal;
77 j=j+1;
78 printf(".");
79 fflush(stdout);
80 sleep(1);
81 myglobal=j;
82 pthread_mutex_unlock(&amp;mymutex);
83 }
84 return NULL;
85 }
86
87 int main(void) {
88
89 pthread_t mythread;
90 int i;
91
92 if ( pthread_create( &amp;mythread, NULL, thread_function, NULL) ) {
93 printf("error creating thread.");
94 bort();
95 }
96
97 for ( i=0; i&lt;20; i++) {
98 pthread_mutex_lock(&amp;mymutex);
99 myglobal=myglobal+1;
100 pthread_mutex_unlock(&amp;mymutex);
101 printf("o");
102 fflush(stdout);
103 sleep(1);
104 }
105
106 if ( pthread_join ( mythread, NULL ) ) {
107 printf("error joining thread.");
108 abort();
109 }
110
111 printf("\nmyglobal equals %d\n",myglobal);
112
113 exit(0);
114
115 }
116 </pre>
117
118 </body>
119 </section>
120 <section>
121 <title>Tempo di capire</title>
122 <body>
123
124 <p>
125 Se paragonate questo codice con la versione del mio <uri
126 link="/doc/it/articles/l-posix1.xml">precedente articolo</uri>, noterete
127 l'aggiunta delle chiamate a pthread_mutex_lock() e pthread_mutex_unlock().
128 Queste chiamate compiono una funzione fondamentale nei programmi con thread.
129 Forniscono un metodo di mutua esclusione (da cui il nome). Due thread non
130 possono avere lo stesso mutex bloccato nello stesso momento.
131 </p>
132
133 <p>
134 I mutex lavorano così. Se il thread "a" prova a bloccare un mutex mentre il
135 thread "b" ha lo stesso mutex bloccato, il thread "a" va in sleep. Appena il
136 thread "b" rilascia il mutex (grazie ad una chiamata a pthread_mutex_unlock()),
137 il thread "a" riuscirà a bloccare il mutex (in altre parole ritornerà dalla
138 chiamata a pthread_mutex_lock() con il mutex bloccato). Allo stesso modo se il
139 thread "c" prova a bloccare il mutex mentre il thread "a" lo sta tenendo,
140 anche il thread "c" andrà temporaneamente in sleep. Tutti i thread che vanno in
141 sleep, avendo chiamato pthread_mutex_lock() su di un mutex già bloccato si
142 mettono in coda per l'accesso a quel mutex.
143 </p>
144
145 <p>
146 pthread_mutex_lock() e pthread_mutex_unlock() sono usati di norma per proteggere
147 strutture di dati. Cioè, siete sicuri che solo un thread alla volta possa
148 accedere ad una certa struttura dati bloccandola e sbloccandola. Come potete
149 aver già indovinato, la libreria POSIX dei thread garantisce un blocco senza
150 dover mettere il thread in sleep se un thread prova a bloccare un mutex
151 sbloccato.
152 </p>
153
154 <figure link="/images/docs/l-posix-mutex.gif" caption="Per il vostro piacere, 4
155 znurts rimettono in atto una scena da recenti chiamate pthread_mutex_lock()"/>
156
157 <p>
158 Il thread in questa immagine che ha il mutex bloccato può accedere alla
159 complessa struttura dati senza doversi preoccupare di avere altri thread che
160 ci provino allo stesso tempo. La struttura dati è effettivamente "congelata"
161 fino a che il mutex non viene sbloccato. È come se le chiamate a
162 pthread_mutex_lock() e pthread_mutex_unlock() fossero cartelli di "lavori in
163 corso" che delimitano una particolare parte dei dati condivisi che è stata
164 modificata o letta. Le chiamate si comportano come avvisi per gli altri thread
165 per mandarli in sleep e aspettare il loro turno per bloccare il mutex.
166 Ovviamente questo è vero se si delimita ciascuna "read" e "write" ad una
167 particolare struttura dati con chiamate a pthread_mutex_lock() e
168 pthread_mutex_unlock().
169 </p>
170
171 </body>
172 </section>
173 <section>
174 <title>Ma perché i mutex?</title>
175 <body>
176
177 <p>
178 Sembra interessante, ma perché vogliamo mettere i nostri thread in sleep? Dopo
179 tutto, il principale pregio dei thread non è proprio la loro abilità nel
180 lavorare indipendentemente ed in molti casi contemporaneamente? Certo, questo è
181 assolutamente vero. Tuttavia, ciascun programma con thread non banali richiede
182 almeno un certo uso dei mutex.un po' di uso dei mutex. Rivediamo il nostro
183 programma dell'esempio per capire perché. Se date un'occhiata a
184 thread_function(), noterete che il mutex è bloccato all'inizio del ciclo e
185 rilasciato alla fine. In questo esempio, mymutex è usato per proteggere il
186 valore di myglobal.
187 </p>
188
189 <p>
190 Se guardate attentamente thread_function(), noterete che il codice relativo
191 all'incremento copia myglobal su di una variabile locale, incrementa la
192 variabile locale, va in sleep per un secondo, e solo allora copia il valore
193 della variabile locale su myglobal. Senza il mutex
194 thread_function()sovrascriverebbe il valore incrementato quando si sveglia, se
195 il nostro thread principale incrementa myglobal durante il breve sonno di un
196 secondo di thread_function(). Usare un mutex assicura che questo non succeda
197 (Nel caso voi vi stiate chiedendo, ho aggiunto il ritardo di un secondo per
198 mostrare un risultato difettoso. Non c'è nessuna motivo perché thread_function()
199 vada in sleep per un secondo prima di riscrivere il valore della variabile
200 locale su myglobal). Il nostro nuovo programma usando il mutex produce l'effetto
201 desiderato.
202 </p>
203
204 <pre caption="Output del programma che usa il mutex">
205 $ <i>./thread3</i>
206 o..o..o.o..o..o.o.o.o.o..o..o..o.ooooooo
207 myglobal vale 40
208 </pre>
209
210 <p>
211 Per esplorare ulteriormente questo concetto estremamente importante, diamo
212 un'occhiata alla parte di codice del nostro programma che esegue l'incremento:
213 </p>
214
215 <pre caption="Codice per l'incremento">
216 thread_function() increment code:
217 j=myglobal;
218 j=j+1;
219 printf(".");
220 fflush(stdout);
221 sleep(1);
222 myglobal=j;
223
224 main thread increment code:
225 myglobal=myglobal+1;
226 </pre>
227
228 <p>
229 Se questo codice fosse in un programma a singolo thread ci si aspetterebbe che
230 il codice thread_function() fosse completamente eseguita. L'esecuzione verrebbe
231 allora seguita dal codice principale del thread (o nella maniera inversa). In un
232 programma con thread ma senza mutex, il codice può (e spesso lo farà, grazie
233 alla chiamata a sleep()) finire l'esecuzione in quest'ordine:
234 </p>
235
236 <pre caption="Ordine di esecuzione">
237 thread_function() thread main thread
238
239 j=myglobal;
240 j=j+1;
241 printf(".");
242 fflush(stdout);
243 sleep(1); myglobal=myglobal+1;
244 myglobal=j;
245 </pre>
246
247 <p>
248 Quando il codice viene eseguito in questo particolare ordine la modifica fatta
249 dal thread principale alla variabile myglobal viene sovrascritta. In questo modo
250 finiamo con un valore non corretto alla fine del nostro programma. Se stessimo
251 manipolando dei puntatori probabilmente finiremmo con un segfault. Notate che il
252 nostro thread di thread_function() esegue tutte le sue istruzioni in ordine. Non
253 è come se thread_function() facesse qualche cosa non in ordine. Il problema è
254 che abbiamo un altro thread che sta facendo altre modifiche alla stessa
255 struttura dati nello stesso tempo.
256 </p>
257
258 </body>
259 </section>
260 <section>
261 <title>Dentro i thread 1</title>
262 <body>
263
264 <p>
265 Prima che vi spieghi come trovare il modo di usare i mutex, vi offro un piccolo
266 sguardo del lavoro interno dei thread. Ecco il nostro primo esempio:
267 </p>
268
269 <p>
270 Diciamo che voi abbiate un thread principale che crea tre nuovi thread: i thread
271 "a", "b" e "c". Decidiamo che il thread "a" sia stato creato per primo, il
272 thread "b" per secondo e il thread "c" per ultimo.
273 </p>
274
275 <pre caption="Ordine di creazione dei thread">
276 pthread_create( &amp;thread_a, NULL, thread_function, NULL);
277 pthread_create( &amp;thread_b, NULL, thread_function, NULL);
278 pthread_create( &amp;thread_c, NULL, thread_function, NULL);
279 </pre>
280
281 <p>
282 Dopo che la prima chiamata a pthread_create() è completata, voi potete
283 immaginare che, o il thread "a" esiste o, che ha finito e che è ora fermo. Dopo
284 la seconda chiamata a pthread_create(), sia il thread principale che il thread
285 "b" possono pensare che il thread "a" esista ( o sia fermo).
286 </p>
287
288 <p>
289 Tuttavia, immediatamente dopo che la seconda chiamata a create() ritorna, il
290 thread principale non può immaginare quale thread (a o b) stia per partire per
291 primo. Benché ambedue i thread esistano è a discrezione del kernel e della
292 libreria dei thread dargli un intervallo di tempo di CPU. Non c'è una regola
293 fissa su chi parta per primo. Ora è molto probabile che il thread "a" incominci
294 l'esecuzione prima del thread "b", ma non è garantito. Questo è specialmente
295 vero su macchine multi-processore. Se voi scrivete del codice che assuma che il
296 thread "a" effettivamente incominci la sua esecuzione prima del thread "b", vi
297 ritroverete con un programma che funzionerà il 99% delle volte. O peggio, un
298 programma che funziona il 100% delle volte sulla vostra macchina ma mai ( 0%)
299 sul server a quattro processori del vostro cliente.
300 </p>
301
302 <p>
303 Un'altra cosa che possiamo imparare da questo esempio è che la libreria dei
304 thread conserva l'ordine di esecuzione del codice per ciascun thread
305 individuale. In altre parole, queste tre chiamate a pthread_create() verranno
306 eseguite nell'ordine in cui compaiono. Dal punto di vista del thread principale,
307 tutto il codice sta lavorando secondo ordine. Qualche volta possiamo sfruttare
308 questo per ottimizzare parte dei nostri programmi con thread. Come nell'esempio
309 sopra il thread "c" può pensare che i thread "a" e "b" stiano girando o abbiano
310 già terminato. Non ci si deve preoccupare della possibilità che i thread "a"
311 o "b" non siano ancora stati creati. Potete usare questa logica per ottimizare
312 i vostri programmi con i thread.
313 </p>
314
315 </body>
316 </section>
317 <section>
318 <title>Dentro i thread 2</title>
319 <body>
320
321 <p>
322 OK, adesso come altro ipotetico esempio, supponiamo di avere un po' di thread
323 che stanno eseguendo il codice qui sotto:
324 </p>
325
326 <pre caption="Codice in esecuzione">
327 myglobal=myglobal+1;
328 </pre>
329
330 <p>
331 Abbiamo bisogno di bloccare/sbloccare il mutex rispettivamente prima e dopo
332 l'incremento? Alcuni di voi potrebbe dire di no. Il compilatore, dopo tutto,
333 molto probabilmente compilerà l'assegnazione sopra in un unica istruzione
334 macchina. Come sapete una singola istruzione macchina non può essere interrotta
335 "mid-stream". Anche gli interrupt hardware rispettano l'atomicità delle
336 istruzioni macchina. A causa di questa tendenza si può essere tentati di
337 lasciare fuori le chiamate pthread_mutex_lock() e pthread_mutex_unlock(). Non
338 fatelo.
339 </p>
340
341 <p>
342 Sono stato un debole? Non proprio. Primo non dovreste presumere che
343 l'assegnamento sopra sarà compilato come una singola istruzione macchina, a meno
344 che non lo abbiate verificato di persona. Anche se inserite alcune parti scritte
345 in assembler per essere sicuri che l'incremento avvenga atomicamente o anche se
346 scrivete il compilatore da voi, potete ancora avere dei problemi.
347 </p>
348
349 <p>
350 Ecco perché. Usare una singola inline assembler opcode probabilmente funzionerà
351 magnificamente su di una macchina con un unico processore. Ciascun incremento
352 avviene atomicamente ed è probabile che il risultato sarà quello desiderato. Ma
353 su di una macchina multiprocessore è un'altra storia. Con le macchine con più
354 CPU potete avere due processori distinti che eseguono la assegnazione sopra
355 quasi (o esattamente) allo stesso tempo. E ricordatevi anche che questa modifica
356 della memoria ha bisogno di passare dalla cache L1 alla L2 e poi alla memoria
357 principale. (Una macchina SMP non è solo un processore addizionale; ha anche
358 bisogno di hardware speciale per arbitrare l'accesso alla RAM). Alla fine, voi
359 non avete idea di quale CPU vinca la "gara" per scrivere nella memoria
360 principale. Per produrre codice prevedibile, voi volete usare i mutex. I mutex
361 inseriscono una "memory barrier", che assicura che le scritture nella memoria
362 principale avvengano nell'ordine in cui i thread bloccano i mutex.
363 </p>
364
365 <p>
366 Considerate una architettura SMP che aggiorni la memoria principale in blocchi
367 da 32-bit. Se state incrementando un intero a 64-bit senza i mutex, i quattro
368 byte più significativi possono provenire da una CPU e gli altri quattro da
369 un'altra. Male! Peggio ancora, usando questa tecnica scadente, il vostro
370 programma fallirà una volta ad ogni morte di papa o anche alle tre di mattina
371 sul sistema di un vostro importante cliente. David R. Butenhof, nel suo libro,
372 Programming with POSIX Threads (vedi <uri link="#resources">Risorse</uri> alla
373 fine dell'articolo), contempla tutte le permutazioni possibili quando non si
374 usano i mutex.
375 </p>
376
377 </body>
378 </section>
379 <section>
380 <title>Troppi mutex</title>
381 <body>
382
383 <p>
384 Se usate troppi mutex, il vostro codice non avrà nessun tipo di concorrenza
385 e girerà più lentamente di uno con una soluzione single-threaded. Se ne mettete
386 troppo pochi, il vostro codice avrà bug strani ed imbarazzanti. Fortunatamente
387 c'è una via di mezzo. Prima di tutto i mutex sono usati per rendere seriale
388 l'accesso a dati condivisi. Non usateli per dati non condivisi e neanche se la
389 logica del vostro programma assicura che solo un thread abbia accesso ad una
390 certa struttura dati in un determinato tempo.
391 </p>
392
393 <p>
394 In secondo luogo se state usando dati condivisi, usate i mutex sia in lettura
395 che in scrittura. Circondate le vostre sezioni di lettura e scrittura con
396 pthread_mutex_lock() e pthread_mutex_unlock(), o usatele ogni volta che un
397 invariante del programma è momentaneamente rotto. Imparate a vedere il vostro
398 codice secondo la prospettiva di un singolo thread e assicuratevi che ciascun
399 thread nel vostro programma abbia una buona visione della memoria. Probabilmente
400 impiegherete molto tempo prima di abituarvi a scrivere del codice con i mutex,
401 ma presto diventeranno una seconda natura per voi e sarete capaci di usarli in
402 modo corretto senza doverci pensare <e>troppo</e>.
403 </p>
404
405 </body>
406 </section>
407 <section>
408 <title>Usare le chiamate: inizializzazione</title>
409 <body>
410
411 <p>
412 OK, adesso è giunto il momento di vedere tutti i possibili utilizzi dei mutex.
413 Primo: incominciamo con l'inizializzazione. Nel nostro <uri
414 link="#thread3c">esempio thread3.c</uri>,, usiamo un metodo di inizializzazione
415 statico. Questo richiede la dichiarazione di una variabile pthread_mutex_t e
416 assegnarle il valore della costante PTHREAD_MUTEX_INITIALIZER:
417 </p>
418
419 <pre caption="Esempio di inizializzazione">
420 pthread_mutex_t mymutex=PTHREAD_MUTEX_INITIALIZER;
421 </pre>
422
423 <p>
424 Questo è abbastanza semplice. Ma si possono creare mutex anche dinamicamente.
425 Usate questo metodo dinamico ogni volta che il vostro codice alloca un nuovo
426 mutex usando malloc(). In questo caso il metodo di inizializzazione statico
427 non funzionerebbe e dovrebbe venir usata la routine pthread_mutex_init():
428 </p>
429
430 <pre caption="Creare un mutex in modo dinamico">
431 int pthread_mutex_init( pthread_mutex_t *mymutex, const
432 pthread_mutexattr_t*attr)
433 </pre>
434
435 <p>
436 Come potete notare, pthread_mutex_init accetta un puntatore a una regione di
437 memoria preventivamente allocata per inizializzarlo come mutex. Come secondo
438 argomento, può anche accettare un puntatore opzionale pthread_mutexattr_t.
439 Questa struttura può essere usata per impostare diversi attributi del mutex. Ma
440 di norma questi attributi non sono necessari, così generalmente si specifica
441 NULL.
442 </p>
443
444 <p>
445 Ogni volta che inizializzate un mutex usando pthread_mutex_init(), dovrebbe
446 essere distrutto usando pthread_mutex_destroy(). pthread_mutex_destroy() accetta
447 un unico puntatore a pthread_mutex_t e libera tutte le risorse impegnate dal
448 mutex al momento della sua creazione. State attenti che pthread_mutex_destroy()
449 non liberi la memoria usata per salvare pthread_mutex_t. Dipende da voi svuotare
450 la memoria. Ricordatevi anche che sia pthread_mutex_init() che
451 pthread_mutex_destroy() ritornano zero se tutto va bene.
452 </p>
453
454 </body>
455 </section>
456 <section>
457 <title>Usare le chiamate: locking</title>
458 <body>
459
460 <pre caption="Esempio di locking">
461 pthread_mutex_lock(pthread_mutex_t *mutex)
462 </pre>
463
464 <p>
465 pthread_mutex_lock() accetta un unico puntatore ad un mutex per bloccarlo. Se il
466 mutex fosse già bloccato, il chiamante andrà in sleep. Quando la funzione
467 ritorna il chiamante sarà (ovviamente) svegliato e a questo punto terrà anche il
468 lock. Questa chiamata può tornare uno zero, in caso di successo, o un codice di
469 errore diverso da zero in caso di fallimento.
470 </p>
471
472 <pre caption="Esempio di unlocking">
473 pthread_mutex_unlock(pthread_mutex_t *mutex)
474 </pre>
475
476 <p>
477 pthread_mutex_unlock() complementa pthread_mutex_lock() e sblocca un mutex ch
478 e il thread aveva già bloccato. Dovreste sempre sbloccare un mutex che avete
479 bloccato quando è sufficientemente sicuro (per aumentarne le prestazione). E non
480 dovreste mai sbloccare un mutex di cui non abbiate il blocco (altrimenti la
481 chiamata a pthread_mutex_unlock() fallirà con un valore di ritorno EPERM diverso
482 da zero).
483 </p>
484
485 <pre caption="Provando l'esempio di lock">
486 pthread_mutex_trylock(pthread_mutex_t *mutex)
487 </pre>
488
489 <p>
490 Questa chiamata è comoda quando volete bloccare un mutex mentre il vostro thread
491 sta facendo qualcos'altro perché il mutex è al momento bloccato. Quando chiamate
492 pthread_mutex_trylock() cercherete di bloccare il mutex. Se il mutex è al
493 momento sbloccato voi lo bloccate e questa funzione ritornerà zero. Tuttavia, se
494 il mutex è bloccato, questa chiamata non lo bloccherà. Ritornerà invece un
495 valore di errore EBUSY, diverso da zero. A questo punto proseguite con le vostre
496 attività e provate a bloccarlo più tardi.
497 </p>
498
499 </body>
500 </section>
501 <section>
502 <title>Aspettando le condizioni</title>
503 <body>
504
505 <p>
506 I mutex sono strumenti necessari per i programmi con thread, ma non possono fare
507 tutto. Cosa succede, ad esempio, se il vostro thread sta aspettando che si
508 verifichi una certa condizione su dei dati condivisi? Il vostro codice sblocca e
509 blocca ripetutamente il mutex, controllando ogni cambiamento del valore. Allo
510 stesso tempo sbloccherà velocemente il mutex così che altri possano fare i
511 cambiamenti necessari. Ma questo è un approccio orribile, perché questo thread
512 dovrà fare un busy loop per determinare un cambiamento in un ragionevole lasso
513 di tempo.
514 </p>
515
516 <p>
517 Potete mettere il thread chiamante in sleep per un po', diciamo tre secondi tra
518 ogni controllo, ma allora il vostro codice con thread non sarà reattivo in
519 maniera ottimale. Quello di cui avete bisogno è un modo per mettere in sleep un
520 thread mentre aspetta che alcune condizioni vengano a verificarsi. Una volta che
521 si verificano le condizioni avete bisogno di un metodo per svegliare i o il
522 thread che sta aspettando che quella condizione si verifichi. Se riuscite a fare
523 ciò il vostro codice con thread sarà veramente efficiente e non vincolerà
524 importanti bloccaggi di mutex. Questo è ciò che le variabili della condizione
525 POSIX possono fare per voi.
526 </p>
527
528 <p>
529 E le variabili della condizione POSIX sono l'argomento del mio prossimo
530 articolo, dove vi mostrerà esattamente come usarle. A quel punto avrete tutte le
531 risorse per creare sofisticati programmi con thread che modellano gruppi di
532 lavoro, linee di assembler e altro ancora. Nel nuovo articolo accelererò il
533 passo ora che avete più familiarità con i thread. Spero di riuscire ad inserire
534 un programma con thread ragionevolmente sofisticato alla fine del prossimo
535 articolo e parlando di cura delle condizioni, arrivederci!
536 </p>
537
538 </body>
539 </section>
540 </chapter>
541
542 <chapter id="resources">
543 <title>Risorse</title>
544 <section>
545 <body>
546
547 <ul>
548 <li>
549 Leggi la spiegazione ai thread POSIX di Daniel <uri
550 link="l-posix1.xml">Parte 1</uri> e <uri link="l-posix3.xml">Parte
551 3</uri>.
552 </li>
553 <li>
554 Leggete la documentazione su <uri
555 link="http://metalab.unc.edu/pub/Linux/docs/faqs/Threads-FAQ/html/">Linux
556 threads</uri>, di Sean Walton, KB7rfa.
557 </li>
558 <li>
559 Date sempre un'occhiata all'amichevole pagina del manuale LINUX di pthread
560 (<c>man -k pthread</c>).
561 </li>
562 <li>
563 Guardate <uri link="http://pauillac.inria.fr/~xleroy/linuxthreads/">The
564 LinuxThreads Library.</uri>
565 </li>
566 <li>
567 <uri link="http://www.users.itl.net.ua/~prool/proolix.html">Proolix</uri> è
568 un semplice sistema operativo POSIX-compliant per i8086+ in continuo
569 sviluppo.
570 </li>
571 <li>
572 Date un'occhiata al libro di David R. Butenhof <uri
573 link="http://www.amazon.com/exec/obidos/ASIN/0201633922/o/qid=961544788/sr=8-1/ref=aps_sr_b_1_1/002-2882413-1227240">
574 Programming with POSIX Threads</uri>, in cui lui affronta, tra le altre
575 cose, le possibili permutazioni del non usare i mutex.
576 </li>
577 <li>
578 Prendete il libro di W. Richard Stevens <!-- FIXME not available out
579 there,
580 commenting out and leaving finding it pleasure to readers<uri
581
582 link="http://devworks.krcinfo.com/WebForms/ProductDetails.aspx?ProductID=
583 0139498761">-->"UNIX Network Programming".
584 </li>
585 <li>
586 Trovate ulteriori risorse per sviluppatore Linux in <uri
587 link="http://www.ibm.com/developerworks/linux/">developerWorks Linux
588 zone</uri>.
589 </li>
590 <li>
591 Fatevi coinvolgere dalla comunità di developerWorks partecipando ai
592 <uri link="http://www.ibm.com/developerworks/blogs/">blog di
593 developerWorks</uri>.
594 </li>
595 <!-- FIXME Ugly advertisement
596 15:25 <rane> is that ok to put such advertisment at gentoo.org?
597 15:26 * neysx would not copy them
598 15:27 <SwifT> I wouldn't keep it
599 <li>
600 Purchase <uri
601
602 link="http://devworks.krcinfo.com/WebForms/ProductList.aspx?Search=Category&id=
603 300&parent=Linux">Linux
604 books at discounted prices</uri> in the Linux section of the
605 Developer Bookstore.
606 </li>
607 <li>
608 Order the no-charge SEK for Linux, a two-DVD set containing the latest IBM
609 trial software for Linux from DB2&registered;, Lotus&registered;,
610 Rational&registered;, Tivoli&registered;, and WebSphere&registered;.
611 </li>
612 <li>
613 Innovate your next Linux development project with <uri
614 link="http://www.ibm.com/developerworks/downloads/?S_TACT=105AGX03">IBM
615 trial software
616 </uri>, available for download directly from developerWorks.
617 </li>
618 -->
619 </ul>
620
621 </body>
622 </section>
623 </chapter>
624 </guide>
625
626
627
628 --
629 gentoo-commits@l.g.o mailing list