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 <pthread.h> |
65 |
#include <stdlib.h> |
66 |
#include <unistd.h> |
67 |
#include <stdio.h> |
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<20; i++ ) { |
75 |
pthread_mutex_lock(&mymutex); |
76 |
j=myglobal; |
77 |
j=j+1; |
78 |
printf("."); |
79 |
fflush(stdout); |
80 |
sleep(1); |
81 |
myglobal=j; |
82 |
pthread_mutex_unlock(&mymutex); |
83 |
} |
84 |
return NULL; |
85 |
} |
86 |
|
87 |
int main(void) { |
88 |
|
89 |
pthread_t mythread; |
90 |
int i; |
91 |
|
92 |
if ( pthread_create( &mythread, NULL, thread_function, NULL) ) { |
93 |
printf("error creating thread."); |
94 |
bort(); |
95 |
} |
96 |
|
97 |
for ( i=0; i<20; i++) { |
98 |
pthread_mutex_lock(&mymutex); |
99 |
myglobal=myglobal+1; |
100 |
pthread_mutex_unlock(&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( &thread_a, NULL, thread_function, NULL); |
277 |
pthread_create( &thread_b, NULL, thread_function, NULL); |
278 |
pthread_create( &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®istered;, Lotus®istered;, |
610 |
Rational®istered;, Tivoli®istered;, and WebSphere®istered;. |
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 |