Professional Documents
Culture Documents
Les threads
4.1 Introduction
Le modle de processus dcrit au chapitre prcdent est un programme
qui sexcute selon un chemin unique avec un seul compteur ordinal. On
dit quil a un flot de contrle unique ou un seul thread. De nombreux systmes dexploitation modernes offrent la possibilit dassocier un mme
processus plusieurs chemins dexcution ou multithread (figure 4.1). Ils
permettent ainsi lexcution simultane des parties dun mme processus.
Chaque partie correspond un chemin dexcution du processus. Le processus est vu comme tant un ensemble de ressources (code excutable,
segments de donnes, de fichiers, de priphriques, etc.) que ces parties appeles flots de contrle ou processus lgers (threads en anglais) partagent.
Chaque flot de contrle (thread) a cependant, en plus des ressources communes, sa propre zone de donnes ou de variables locales, sa propre pile
dexcution, ses propres registres et son propre compteur ordinal.
4.1.1 Avantages
Comparativement aux processus un flot de contrle unique, un thread
ou processus lger avec plusieurs flots de contrle prsente plusieurs avantages, notamment :
Ractivit. Le processus lger continue sexcuter mme si certaines
de ses parties sont bloques.
Partage de ressources.
conomie despace mmoire et de temps. Par exemple sous Solaris,
la cration dun processus est 30 fois plus lente que celle dun proces1
un processus
un thread
multiples processus
un thread par processus
un processus
multiples threads
multiples processus
multiples threads par processus
Le service pthread_create() cre un processus lger qui excute la fonction nomfonction avec largument arg et les attributs attr. Les attributs
permettent de spcifier la taille de la pile, la priorit, la politique de planification, etc. Il y a plusieurs formes de modification des attributs.
Suspension de threads
int pthread_join(pthread_t *thid,
void **valeur_de_retour);
// pour s l e e p
// p t h r e a d _ c r e a t e , p t h r e a d _ j o i n , p t h r e a d _ e x i t
while ( 1 ) ; // f o r e v e r
r e t u r n NULL;
i n t main ( )
{
pthread_t thread ;
p r i n t f ( " pid de main = %d\n " , ( i n t ) g e t p i d ( ) ) ;
p t h r e a d _ c r e a t e (& t h r e a d , NULL, & f o n c t i o n , NULL) ;
while ( 1 ) ; // f o r e v e r
20
return 0 ;
TIME COMMAND
0:00 -tcsh
0:53 pthread-pid
0:00 pthread-pid
0:53 pthread-pid
0:00 ps x
pthread-pid
10
void a f f i c h e r ( i n t n , char l e t t r e )
{
int i , j ;
f o r ( j = 1 ; j <n ; j + + ) {
for ( i = 1 ; i < 1 0 0 0 0 0 0 0 ; i ++);
p r i n t f ( "%c " , l e t t r e ) ;
f f l u s h ( stdout ) ;
}
}
20
threadA ( void
void
{
inutilise )
a f f i c h e r ( 1 0 0 , A ) ;
p r i n t f ( "\n Fin du t h r e a d A\n " ) ;
f f l u s h ( stdout ) ;
p t h r e a d _ e x i t (NULL) ;
30
p t h r e a d _ e x i t (NULL) ;
void
threadB ( void
inutilise )
{
p t h r e a d _ t thC ;
p t h r e a d _ c r e a t e (&thC , NULL , threadC , NULL) ;
50
afficher (100,B ) ;
p r i n t f ( "\n Le t h r e a d B a t t e n d l a f i n du t h r e a d C\n " ) ;
p t h r e a d _ j o i n ( thC ,NULL) ;
p r i n t f ( "\n Fin du t h r e a d B\n " ) ;
f f l u s h ( stdout ) ;
p t h r e a d _ e x i t (NULL) ;
60
}
i n t main ( )
{
int i ;
p t h r e a d _ t thA , thB ;
p r i n t f ( " C r e a t i o n du t h r e a d A" ) ;
p t h r e a d _ c r e a t e (&thA , NULL , threadA , NULL) ;
p t h r e a d _ c r e a t e (& thB , NULL , threadB , NULL) ;
sleep ( 1 ) ;
70
// a t t e n d r e que l e s t h r e a d s a i e n t termine
p r i n t f ( " Le t h r e a d p r i n c i p a l a t t e n d que l e s a u t r e s se t e r m i n e n t \n " ) ;
p t h r e a d _ j o i n ( thA ,NULL) ;
p t h r e a d _ j o i n ( thB ,NULL) ;
80
exit (0);
Exemple 3. Le programme threads.cpp montre lutilisation des variables globales, ainsi que le passage de paramtres lors des appels de threads.
Listing 4.3 threads.cpp
# include < u n i s t d . h >
# include < pthread . h >
// s l e e p
// p t h r e a d _ c r e a t e , p t h r e a d _ j o i n , p t h r e a d _ e x i t
8
# include < s t d i o . h>
i n t glob = 0 ;
void increment ( void
) ; void
decrement ( void
);
10
i n t main ( )
{
pthread_t tid1 , tid2 ;
p r i n t f ( " i c i main[%d ] , glob = %d\n " , g e t p i d ( ) , glob ) ;
// c r a t i o n d un t h r e a d pour increment
i f ( p t h r e a d _ c r e a t e (& t i d 1 , NULL , increment , NULL ) ! = 0 )
return
1;
p r i n t f ( " i c i main : c r e a t i o n du t h r e a d[%d ] avec s u c c s \n " , t i d 1 ) ;
// c r a t i o n d un t h r e a d pour decrement
i f ( p t h r e a d _ c r e a t e (& t i d 2 , NULL , decrement , NULL ) ! = 0 )
return
1;
p r i n t f ( " i c i main : c r e a t i o n du t h r e a d [%d ] avec s u c c s \n " , t i d 2 ) ;
20
}
30
// a t t e n d r e l a f i n des t h r e a d s
p t h r e a d _ j o i n ( t i d 1 ,NULL) ;
p t h r e a d _ j o i n ( t i d 2 ,NULL) ;
p r i n t f ( " i c i main : f i n des t h r e a d s , glob = %d \ n " , glob ) ;
return 0 ;
decrement ( void
void
{
i n t dec = 1 ;
sleep ( 1 ) ;
glob = glob
dec ;
p r i n t f ( " i c i decrement [%d ] , glob = %d\n " , p t h r e a d _ s e l f ( ) , glob ) ;
p t h r e a d _ e x i t (NULL) ;
}
void
{
40
increment ( void
leibnitz> pthread
ici main[21939], glob = 0
ici main : creation du thread[1026] avec succs
ici main : creation du thread [2051] avec succs
ici increment[21941], glob = 2
ici decrement[21942], glob = 1
ici main : fin des threads, glob = 1
leibnitz>
< s t d i o . h>
< u n i s t d . h>
< s t d l i b . h>
< pthread . h>
i n t main ( )
{
int res ;
pthread_t a_thread ;
void t h r e a d _ r e s u l t ;
20
30
r e s = p t h r e a d _ c r e a t e (& a _ t h r e a d , NULL , t h r e a d _ f u n c t i o n ,
( void ) message ) ;
i f ( res ! = 0 )
{
p e r r o r ( " Thread c r e a t i o n f a i l e d " ) ;
e x i t ( EXIT_FAILURE ) ;
}
p r i n t f ( " En a t t e n t e que l e t h r e a d termine . . . \ n " ) ;
res = pthread_join ( a_thread , & thread_result ) ;
i f ( res ! = 0 )
{
p e r r o r ( " Echec dans l union du t h r e a d " ) ;
e x i t ( EXIT_FAILURE ) ;
}
p r i n t f ( " Thread termine % s\n " , ( char ) t h r e a d _ r e s u l t ) ;
p r i n t f ( " Le message e s t % s\n " , message ) ;
e x i t ( EXIT_SUCCESS ) ;
10
}
void
{
40
t h r e a d _ f u n c t i o n ( void
arg )
Algorithmes vectoriels
Il y a plusieurs exemples dutilisation des threads pour le traitement
parallle de linformation. Un serveur de fichiers 1 par exemple, ncessite
la gestion et la manipulation de plusieurs requtes en mme temps, qui
peuvent tre effectues par lutilisation des threads. Ainsi, la cration dune
thread chaque requte est envisageable : les threads effectuent les traitements ncessaires et envoient les rsultats au client.
La manipulation des matrices et des vecteurs est un autre champ particulirement propice pour les algorithmes parallles. La multiplication de
deux matrices a et b de colonnes = lignes = 3, par exemple, peut tre
paralllis, puisque :
!
$#
"
!
"
!
"
&%'(
&%)
"
*
"
+%'(
,+%-
"!
"
.%-
+%-
"
"
&%'/
&%)"
*
"
+%'/,+%-"!
"
.%-+%-"
%'
%)
%' %-
%- %-
1
11
10
20
// T a i l l e des m a t r i c e s
// Nb Maximum
30
) j );
12
40
imprime (A ) ;
p r i n t f ( "\n\nMatrice B " ) ;
imprime ( B ) ;
p r i n t f ( "\n\n Matriz C=A+B " ) ;
imprime (C ) ;
return 0 ;
50
60
p t h r e a d _ e x i t (NULL ) ;
6
5
7
1
0
7
0
6
9
7
6
5
Matrice
0
9
6
4
B
3
3
8
8
9
5
6
2
7
6
4
3
9
12
6
8
16
13
10
8
Matriz C=A+B
6
9
14
8
8
15
8
9
13
leibnitz>
Exemple 6. Le programme threads.cpp montre lutilisation des variables globales et le passage de paramtres.
i n t glob = 0 ;
void increment ( void
10
// s l e e p
// p t h r e a d _ c r e a t e , p t h r e a d _ j o i n , p t h r e a d _ e x i t
) ; void
decrement ( void
);
i n t main ( )
{
pthread_t tid1 , tid2 ;
p r i n t f ( " i c i main[%d ] , glob = %d\n " , g e t p i d ( ) , glob ) ;
// c r a t i o n d un t h r e a d pour increment
i f ( p t h r e a d _ c r e a t e (& t i d 1 , NULL , increment , NULL) ! = 0 )
return
1;
p r i n t f ( " i c i main : c r e a t i o n du t h r e a d[%d ] avec s u c c s \n " , t i d 1 ) ;
20
// c r a t i o n d un t h r e a d pour decrement
i f ( p t h r e a d _ c r e a t e (& t i d 2 , NULL , decrement , NULL) ! = 0 )
return
1;
p r i n t f ( " i c i main : c r e a t i o n du t h r e a d [%d ] avec s u c c s \n " , t i d 2 ) ;
// a t t e n d r e l a f i n des t h r e a d s
p t h r e a d _ j o i n ( t i d 1 ,NULL) ;
14
}
30
p t h r e a d _ j o i n ( t i d 2 ,NULL) ;
p r i n t f ( " i c i main : f i n des t h r e a d s , glob = %d \ n " , glob ) ;
return 0 ;
decrement ( void
void
{
i n t dec = 1 ;
sleep ( 1 ) ;
glob = glob
dec ;
p r i n t f ( " i c i decrement [%d ] , glob = %d\n " , p t h r e a d _ s e l f ( ) , glob ) ;
p t h r e a d _ e x i t (NULL) ;
}
void
{
40
increment ( void
Exemple 7.
15
i n t glob = 0 ;
void increment ( void
void decrement ( void
inc ) ;
dec ) ;
i n t main ( )
{
i n t i n c = 2 , dec = 1 ;
p r i n t f ( " i c i main[%d ] , glob = %d\n " , g e t p i d ( ) , glob ) ;
t h r _ c r e a t e (NULL, 0 , increment , & i n c , 0 , NULL) ;
t h r _ c r e a t e (NULL, 0 , decrement , & dec , 0 , NULL) ;
while ( ( t h r _ j o i n (NULL,NULL,NULL) = = 0 ) ) ;
p r i n t f ( " i c i main , f i n des t h r e a d s , glob = %d \ n " , glob ) ;
return 0 ;
}
10
20
30
16
4.3. EXERCICES
17
4.3 Exercices
1. On sait quun thread est cr par un processus parent, mais quelle
diffrence existe-il entre un thread et un processus fils ?
2. Quelle serait un avantage principal utiliser des threads plutt que
des processus fils ?