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