.\" Automatically generated by Pod::Man 2.06 (Pod::Simple 3.04) .\" .\" Standard preamble: .\" ======================================================================== .de Sh \" Subsection heading .br .if t .Sp .ne 5 .PP \fB\\$1\fR .PP .. .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. | will give a .\" real vertical bar. \*(C+ will give a nicer C++. Capital omega is used to .\" do unbreakable dashes and therefore won't be available. \*(C` and \*(C' .\" expand to `' in nroff, nothing in troff, for use with C<>. .tr \(*W-|\(bv\*(Tr .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' 'br\} .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.Sh), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .if \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . nr % 0 . rr F .\} .\" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .hy 0 .if n .na .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "PERLTHRTUT 1" .TH PERLTHRTUT 1 "2006-03-03" "DocFr" "User Contributed Perl Documentation" .SH "NAME/NOM" .IX Header "NAME/NOM" perlthrtut \- Tutoriel sur les threads en Perl .SH "DESCRIPTION" .IX Header "DESCRIPTION" Ce tutoriel de\*'crit les threads a\*` la nouvelle mode introduites dans Perl 5.6.0, appele\*'s threads interpre\*'teur, ou \fBithreads\fR pour faire court. Dans ce mode\*`le, chaque thread exe\*'cute son propre interpre\*'teur Perl, et tout partage de donne\*'es entre les threads doit e\*^tre explicite. .PP Il y a une autre, plus vieille mode de gestion des threads en Perl, appele\*'e le mode\*`le 5.005 ; e\*'videmment, il s'applique aux versions 5.005 de Perl. Ce vieux mode\*`le a des proble\*`mes connus, est obsole\*`te, et sera probablement retire\*' autour de la version 5.10. Vous e\*^tes fortement encourage\*' a\*` faire migrer aussi vite que possible tout code utilisant le mode\*`le 5.005 vers le nouveau mode\*`le. .PP Vous pouvez savoir quel mode\*`le de threads vous avez (ou savoir si vous n'en avez aucun) en exe\*'cutant \f(CW\*(C`perl \-V\*(C'\fR et en examinant la section \&\f(CW\*(C`Platform\*(C'\fR. Si vous avez \f(CW\*(C`useithreads=define\*(C'\fR, vous avez les ithreads ; si vous avez \f(CW\*(C`use5005threads=define\*(C'\fR, vous avez les threads 5.005. Si vous n'avez aucun des deux, votre perl ne contient pas de support pour les threads. Si vous avez les deux, vous avez un proble\*`me. .PP L'acce\*`s aux threads 5.005 se faisait a\*` travers la classe Threads, alors que celui aux ithreads se fait gra\*^ce a\*` la classe threads. Notez le t majuscule et minuscule. .SH "E\*'tat courant" .IX Header "E'tat courant" Le code du mode\*`le ithreads existe depuis Perl 5.6.0, et est conside\*'re\*' stable. L'interface utilisateur aux ithreads (la classe threads) est apparue dans la version 5.8.0, et aujourd'hui on peut la conside\*'rer comme stable, bien qu'il faille la traiter avec pre\*'cautions comme tout ce qui est nouveau. .SH "Qu'est\-ce que c'est qu'un thread, d'abord ?" .IX Header "Qu'est-ce que c'est qu'un thread, d'abord ?" Un thread (\fIprocessus le\*'ger\fR en bon Franc\*,ais) est un flux de contro\*^le a\*` travers un programme, muni d'un point d'exe\*'cution unique. .PP C\*,a ressemble vraiment a\*` un processus, non ? Eh bien, c'est normal. Les threads sont un des composants d'un processus. Chaque processus a au moins un thread, et d'ailleurs jusqu'a\*` aujourd'hui, chaque processus exe\*'cutant Perl a eu un seul thread. Avec Perl 5.8, cependant, vous pouvez cre\*'er des threads supple\*'mentaires. Nous allons voir comment, quand, et pourquoi. .SH "Mode\*`les de programmes utilisant les threads" .IX Header "Mode`les de programmes utilisant les threads" Il y a trois fac\*,on de structurer un programme utilisant des threads. Le choix du mode\*`le de\*'pend de ce que vous voulez que votre programme fasse. Pour de nombreux programmes non triviaux utilisant les threads, vous allez devoir choisir des mode\*`les diffe\*'rents pour les diffe\*'rentes parties de votre programme. .Sh "Mai\*^tre/Esclave" .IX Subsection "Mai^tre/Esclave" Le mode\*`le mai\*^tre/esclave met en sce\*`ne un seul thread \*(L"mai\*^tre\*(R", et un thread \*(L"esclave\*(R" ou plus. Le thread mai\*^tre rassemble et ge\*'ne\*`re les ta\*^ches qui doivent e\*^tre accomplies, et re\*'partit ces ta\*^ches aux threads esclaves. .PP Ce mode\*`le est courant dans les programmes serveurs et a\*` interface graphique, ou\*` un thread principal attend un e\*'ve\*`nement et le passe aux threads esclaves approprie\*'s, pour qu'ils le traitent. Une fois l'e\*'ve\*`nement transmis, le thread mai\*^tre se remet a\*` attendre un autre e\*'ve\*`nement. .PP Le thread mai\*^tre fait assez peu de travail. Me\*^me si les ta\*^ches ne sont pas ne\*'cessairement traite\*'es plus vite qu'avec une autre me\*'thode, celle-ci a tendance a\*` pre\*'senter les meilleurs temps de re\*'ponse a\*` l'utilisateur. .Sh "E\*'quipe de travail" .IX Subsection "E'quipe de travail" Dans le mode\*`le de l'e\*'quipe de travail, plusieurs threads traitent de la me\*^me fac\*,on les diffe\*'rents morceaux de donne\*'es. Cela reproduit le mode de travail du calcul paralle\*`le classique et des processeurs vectoriels, ou\*` une se\*'rie de processeurs font exactement la me\*^me chose a\*` de nombreux morceaux de donne\*'es. .PP Ce mode\*`le est particulie\*`rement utile si le syste\*`me qui exe\*'cute le programme peut distribuer les threads sur les diffe\*'rents processeurs. Il peut aussi e\*^tre utile en lancer de rayons ou dans les moteurs de rendu, quand les threads individuels peuvent renvoyer des re\*'sultats interme\*'diaires pour donner un retour visuel a\*` l'utilisateur. .Sh "Travail a\*` la chai\*^ne" .IX Subsection "Travail a` la chai^ne" Le mode\*`le de travail a\*` la chai\*^ne divise une ta\*^che en une suite d'e\*'tapes, et consiste a\*` passer les re\*'sultats d'une e\*'tape au thread qui s'occupe de l'e\*'tape suivante. Chaque thread fait une chose a\*` chaque morceau de donne\*'es et passe ses re\*'sultats au thread suivant de la chai\*^ne. .PP Ce mode\*`le est surtout conseille\*' si vous avez plusieurs processeurs, de fac\*,on a\*` ce que plusieurs threads s'exe\*'cutent en paralle\*`le, me\*^me si il peut aussi servir dans d'autres contextes. Il permet de garder les ta\*^ches e\*'le\*'mentaires simples, et autorise certaines partie de la chai\*^ne a\*` bloquer (sur des appels syste\*`me d'E/S, par exemple) alors que d'autres parties continuent a\*` tourner. Si vous exe\*'cutez les diffe\*'rentes parties de la chai\*^ne sur des processeurs diffe\*'rents, cela vous permet aussi de tirer avantage du cache de chaque processeur. .PP Ce mode\*`le est aussi pratique pour une forme de programmation re\*'cursive dans laquelle au lieu de s'appeler elle\-me\*^me, une fonction cre\*'e un autre thread. Les ge\*'ne\*'rateurs de nombres premiers ou de Fibonacci profitent bien du mode\*`le de travail a\*` la chai\*^ne. (Une version d'un ge\*'ne\*'rateur de nombres premiers est pre\*'sente\*'e plus bas.) .SH "Imple\*'mentations des threads dans le syste\*`me d'exploitation" .IX Header "Imple'mentations des threads dans le syste`me d'exploitation" Il y a plusieurs fac\*,ons diffe\*'rentes d'imple\*'menter les threads sur un syste\*`me donne\*'. Cela de\*'pend de la marque, et, parfois, de la version du syste\*`me d'exploitation (\s-1SE\s0). Souvent la premie\*`re imple\*'mentation est relativement simple, mais celles des versions suivantes du \s-1SE\s0 sont plus sophistique\*'es. .PP Les informations pre\*'sente\*'es dans cette section sont utiles mais pas indispensables ; vous pouvez donc les sauter si elles vous rebutent. .PP Il y a trois sortes de threads : les threads en mode utilisateur, les threads du noyau, et les threads multiprocesseurs du noyau. .PP Les threads en mode utilisateur vivent entie\*`rement a\*` l'inte\*'rieur d'un programme et de ses bibliothe\*`ques. Dans ce mode\*`le, le \s-1SE\s0 ne sait rien des threads. Pour ce qu'il en voit, votre processus utilisant les threads n'est qu'un processus comme les autres. .PP C'est la fac\*,on la plus facile d'imple\*'menter les threads, et celle avec laquelle commencent la plupart des \s-1SE\s0. Le gros de\*'savantage est que, puisque le \&\s-1SE\s0 ne sait rien des threads, si un des thread bloque, ils bloquent tous. Parmi les activite\*'s bloquantes courantes, on trouve la plupart des appels syste\*`me, des E/S, et les choses comme \fIsleep()\fR. .PP Les threads du noyau sont l'e\*'tape suivante dans l'e\*'volution des threads. Le \s-1SE\s0 connai\*^t des threads ge\*'re\*'s par le noyau et en tient compte. La principale diffe\*'rence entre un thread du noyau et un thread en mode utilisateur est le blocage. Avec les threads du noyau, un thread peut bloquer sans que les autres threads bloquent. Ce n'est pas le cas avec les threads en mode utilisateur, ou\*` le noyau bloque au niveau du processus et pas au niveau du thread. .PP C'est un grand pas en avant, qui peut donner a\*` un programme utilisant les threads un bonus de performance sur les programmes ne les utilisant pas. Les threads qui bloquent au cours d'E/S, par exemple, ne bloqueront pas les threads qui font autre chose. Cependant, chaque processus ne peut avoir qu'un thread en train de s'exe\*'cuter a\*` un instant donne\*', quel que soit le nombre de processeurs que le syste\*`me a. .PP Puisque le noyau peut interrompre un thread a\*` n'importe quel moment, cela va mettre a\*` nu certaines des hypothe\*`ses implicites d'exclusion que vous pouvez faire dans votre programme. Par exemple, quelque chose d'aussi simple que \f(CW\*(C`$a = $a + 2\*(C'\fR peut se comporter de fac\*,on impre\*'visible avec des threads du noyau si \f(CW$a\fR est visible aux autres threads : un autre thread peut en effet changer \f(CW$a\fR entre le moment ou\*` \f(CW$a\fR est lue dans la partie droite, et celui ou\*` la nouvelle valeur est stocke\*'e. .PP Les threads multiprocesseurs du noyau sont l'e\*'tape finale dans l'e\*'volution du support pour les threads. Avec ce mode\*`le, sur une machine a\*` plusieurs processeurs, le \s-1SE\s0 peut faire tourner simultane\*'ment plusieurs threads sur plusieurs processeurs. .PP Cela peut ame\*'liorer se\*'rieusement les performances d'un programme utilisant les threads, puisque plusieurs s'exe\*'cuteront en me\*^me temps. En retour, cependant, tous les fourbes proble\*`mes de synchronisation qui ne se montraient pas avec les threads du noyau vont surgir pour se venger. .PP Au\-dela\*` des diffe\*'rents niveaux d'implication des \s-1SE\s0 dans la gestion des threads, il y a diffe\*'rentes fac\*,on pour un \s-1SE\s0 (et pour une imple\*'mentation des threads sur un me\*^me \s-1SE\s0) d'allouer des cycles processeur aux threads. .PP Les syste\*`mes multita\*^ches coope\*'ratifs obligent les threads qui s'exe\*'cutent a\*` leur rendre le contro\*^le dans deux cas. Un thread peut demander explicitement de ce\*'der le contro\*^le. Il le ce\*`de aussi s'il fait quelque chose de bloquant, comme des E/S. Dans un syste\*`me multita\*^che coope\*'ratif, un thread peut priver tous les autres de temps processeur si il le de\*'cide. .PP Les syste\*`mes multita\*^ches pre\*'emptifs interrompent les threads a\*` intervalle re\*'gulier, et le syste\*`me de\*'cide quelle thread doit e\*^tre exe\*'cute\*' ensuite. Sur un tel syste\*`me, un thread ne peut ge\*'ne\*'ralement pas monopoliser le processeur. .PP Sur certains syste\*`mes, des threads coope\*'ratifs et pre\*'emptifs peuvent tourner simultane\*'ment. (Par exemple, Les threads avec des priorite\*'s temps\-re\*'el se comportent souvent coope\*'rativement, alors que ceux avec des priorite\*'s normales se comportent pre\*'emptivement.) .SH "De quelle sorte sont les threads de Perl ?" .IX Header "De quelle sorte sont les threads de Perl ?" Si vous avez expe\*'rimente\*' d'autres imple\*'mentations des threads, vous pourriez avoir l'impression que les choses ne sont pas ce a\*` quoi vous vous attendiez. Avec les threads de Perl, il est tre\*`s important de garder a\*` l'esprit qu'ils ne sont d'aucune e\*'cole particulie\*`re. Ce ne sont pas des threads \s-1POSIX\s0, ni DecThreads, ni des Green Threads de Java, ni des threads Win32. Il y a des similarite\*'s, et les concepts ge\*'ne\*'raux sont les me\*^mes, mais si vous commencez a\*` mettre votre nez dans les de\*'tails d'imple\*'mentation, vous allez e\*^tre de\*'c\*,u, ou trouble\*'. Les deux peut\-e\*^tre. .PP Cela ne veut pas dire que les threads de Perl sont diffe\*'rents de tout ce qui est venu avant. Le mode\*`le de Perl doit beaucoup aux autres mode\*`les, surtout au mode\*`le \s-1POSIX\s0. Mais de la me\*^me fac\*,on que Perl n'est pas C, les threads Perl ne sont pas les threads \s-1POSIX\s0. Donc, si vous commencez a\*` chercher des mutexes ou des priorite\*'s de threads, prenez du recul et repensez a\*` ce que vous voulez faire, et a\*` comment Perl peut le faire. .PP Cependant, il est important de se souvenir que les threads de Perl ne peuvent pas faire des choses que n'autorise pas votre syste\*`me d'exploitation. Si celui-ci bloque un processus entier sur \fIsleep()\fR, Perl va bloquer aussi. .PP Les Threads de Perl Sont Diffe\*'rents. .SH "Modules re\*'entrants (\fIthread-safe\fP)" .IX Header "Modules re'entrants (thread-safe)" L'ajout des threads a change\*' l'inte\*'rieur de Perl substantiellement. Cela a des implications pour ceux qui e\*'crivent des modules utilisant \s-1XS\s0 ou des bibliothe\*`ques externes. Cependant, dans la mesure ou\*` par de\*'faut les donne\*'es ne sont par partage\*'es par les threads, les modules Perl ont de grandes chances d'e\*^tre de\*'ja\*` re\*'entrants, ou peuvent le devenir facilement. Il faut tester ou passer en revue le code des modules non re\*'entrants avant de les utiliser en production. .PP Tous les modules que vous pouvez utiliser ne sont pas re\*'entrants, et vous devriez toujours supposer qu'un module ne l'est pas, a\*` moins que sa documentation ne dise explicitement le contraire. Cela tient pour les modules qui sont distribue\*'s dans la version standard de Perl. Les threads sont une caracte\*'ristique nouvelle, et me\*^me certains des modules standards ne sont pas re\*'entrants. .PP Me\*^me si un module est re\*'entrant, cela n'implique pas qu'il soit optimise\*' pour fonctionner avec les threads. Il est possible qu'il puisse e\*^tre re\*'e\*'crit pour tirer parti des nouvelles fonctionnalite\*'s lie\*'es aux threads de Perl, pour augmenter les performances dans un environnement utilisant les threads. .PP Si vous utilisez un module qui se trouve n'e\*^tre pas re\*'entrant, vous pouvez vous prote\*'ger en ne l'utilisant que depuis un seul et unique thread. Si vous avez besoin que de plusieurs threads acce\*`dent a\*` ce module, il vous reste a\*` utiliser des se\*'maphores et beaucoup de discipline. Les se\*'maphores sont de\*'crits dans \*(L"Se\*'maphores de base\*(R". .PP Voir aussi \*(L"Re\*'entrance des bibliothe\*`ques syste\*`me\*(R". .SH "Bases des threads" .IX Header "Bases des threads" Le module standard threads fournit les fonctions de base ne\*'cessaires a\*` l'e\*'criture de programmes utilisant les threads. Dans les section suivantes, nous allons en de\*'crire les bases, en vous montrant ce dont vous avez besoin pour faire tourner un programme avec des threads. Apre\*`s c\*,a, nous passerons en revue certaines fonctionnalite\*'s du module threads qui rendent la vie avec les threads plus facile. .Sh "Support de base pour les threads" .IX Subsection "Support de base pour les threads" Le support de Perl pour les threads est une option de compilation \- il est active\*' ou de\*'sactive\*' quand Perl est compile\*' sur votre machine, et non quand vos propres programmes sont compile\*'s. Si votre perl n'a pas e\*'te\*' compile\*' avec le support pour les threads, toute tentative d'utiliser les threads est voue\*'e a\*` l'e\*'chec. .PP Vos programmes peuvent utiliser le module Config pour de\*'terminer si les threads sont active\*'s ou non. Si votre programme ne peut tourner sans eu, vous pouvez e\*'crire quelque chose comme : .PP .Vb 2 \& $Config{useithreads} or die \& "Recompilez Perl avec les threads active\*'s pour faire tourner ce programme."; .Ve .PP Si nous devons faire usage d'un module capable d'utiliser les threads ou non, nous pouvons e\*'crire un programme qui les utilise si possible : .PP .Vb 2 \& use Config; \& use MonModule; \& \& BEGIN { \& if ($Config{useithreads}) { \& # Nous avons les threads \& require MonModule_avec_threads; \& import MonModule_avec_threads; \& } else { \& require MonModule_sans_threads; \& import MonModule_sans_threads; \& } \& } .Ve .PP Du code capable de tourner avec et sans les threads est ge\*'ne\*'ralement tre\*`s touffu, aussi est-il pre\*'fe\*'rable d'isoler le code spe\*'cifique aux threads dans son propre module. Dans l'exemple ci\-dessus, c'est ce que fait MonModule_avec_threads, qui est seulement importe\*' si nous tournons dans un perl avec les threads active\*'s. .Sh "Note a\*` propos des exemples" .IX Subsection "Note a` propos des exemples" Bien que le support pour les threads soit conside\*'re\*' comme stable, il reste un certain nombre de rugosite\*'s qui pourraient vous ge\*^ner si vous essayez les exemples ci\-dessous. Dans une situation re\*'elle, il faudrait prendre garde que tous les threads aient fini avant que le programme ne se termine. Cette pre\*'caution n'a \fBpas\fR e\*'te\*' prise dans nos exemples, par souci de simplicite\*'. Tels quels, il vont produire des messages d'erreur, ge\*'ne\*'ralement a\*` cause de threads qui tournent encore quand le programme se termine. Ne vous en effrayez pas. Les versions futures de Perl re\*'gleront peut\-e\*^tre ce proble\*`me. .Sh "Cre\*'er des threads" .IX Subsection "Cre'er des threads" Le module threads fournit les outils ne\*'cessaires a\*` la cre\*'ation de threads. Comme n'importe quel autre module, vous devez dire a\*` Perl que vous voulez l'utiliser ; \f(CW\*(C`use threads\*(C'\fR importe tous les e\*'le\*'ments dont vous avez besoin pour cre\*'er des threads. .PP La fac\*,on la plus simple de cre\*'er un thread utilise \fInew()\fR : .PP .Vb 1 \& use threads; \& \& $thr = threads\->new(\e&sub1); \& \& sub sub1 { \& print "Dans le thread\en"; \& } .Ve .PP La me\*'thode \fInew()\fR prend une re\*'fe\*'rence a\*` une fonction et cre\*'e un nouveau thread, qui commence a\*` s'exe\*'cuter dans ladite fonction. Le contro\*^le est alors a\*` la fois dans la fonction et dans l'appelant. .PP Si ne\*'cessaire, votre programme peut passer des parame\*`tres a\*` la fonction au de\*'marrage du thread. Il suffit d'inclure la liste des parame\*`tres dans l'appel a\*` \f(CW\*(C`threads::new\*(C'\fR, comme ceci : .PP .Vb 1 \& use threads; \& \& $Param3 = "toto"; \& $thr = threads\->new(\e&sub1, "Param 1", "Param 2", $Param3); \& $thr = threads\->new(\e&sub1, @ListeDeParametres); \& $thr = threads\->new(\e&sub1, qw(Param1 Param2 Param3)); \& \& sub sub1 { \& my @Parametres = @_; \& print "Dans le thread\en"; \& print "Rec\*,u les parame\*`tres >", join("<>", @Parametres), "<\en"; \& } .Ve .PP Cet exemple illustre une autre caracte\*'ristique des threads : vous pouvez ge\*'ne\*'rer plusieurs threads en utilisant la me\*^me fonction. Chaque thread exe\*'cute la me\*^me fonction, mais avec un environnement se\*'pare\*' et des arguments potentiellement diffe\*'rents. .PP \&\f(CW\*(C`create()\*(C'\fR est un synonyme de \f(CW\*(C`new()\*(C'\fR. .Sh "Rendre le contro\*^le" .IX Subsection "Rendre le contro^le" On veut parfois qu'un thread rende explicitement le contro\*^le du processeur a\*` un autre thread. Par exemple, votre syste\*`me de gestion des threads pourrait ne pas supporter le multita\*^che pre\*'emptif, ou vous pourriez e\*^tre en train de faire quelque chose de tre\*`s lourd en calculs, et vouloir e\*^tre su\*^r que le thread de l'interface utilisateur soit appele\*' assez souvent. Et il y a des cas ou\*` l'on veut juste qu'un thread la\*^che le contro\*^le du processeur. .PP Le syste\*`me de threads de Perl fournit la fonction \fIyield()\fR, qui permet de faire ceci. \fIyield()\fR a une utilisation assez e\*'vidente : .PP .Vb 1 \& use threads; \& \& sub boucle { \& my $thread = shift; \& my $toto = 50; \& while($toto\-\-) { print "dans le thread $thread\en" } \& threads\->yield; \& $toto = 50; \& while($toto\-\-) { print "dans le thread $thread\en" } \& } \& \& my $thread1 = threads\->new(\e&boucle, 'premier'); \& my $thread2 = threads\->new(\e&boucle, 'deuxie\*`me'); \& my $thread3 = threads\->new(\e&boucle, 'troisie\*`me'); .Ve .PP Il est important de garder a\*` l'esprit que \fIyield()\fR n'est qu'une suggestion de rendre le contro\*^le du processeur, et qu'il de\*'pend de votre mate\*'riel, \s-1SE\s0 et bibliothe\*`ques de threads que cela arrive re\*'ellement. Par conse\*'quent, il ne faut pas espe\*'rer imposer l'ordonnancement des threads avec des appels a\*` \fIyield()\fR. Cela peut marcher sur votre plateforme, mais cela ne marchera pas sur toutes. .Sh "Attendre qu'un thread termine" .IX Subsection "Attendre qu'un thread termine" Puisque les threads sont aussi des fonctions, elles peuvent renvoyer des valeurs. Pour attendre qu'un thread termine et utiliser les valeurs qu'il peut retourner, vous pouvez vous servir de la me\*'thode \&\fIjoin()\fR : .PP .Vb 1 \& use threads; \& \& $thr = threads\->new(\e&sub1); \& \& @DonneesRenvoyees = $thr\->join; \& print "Le thread a renvoye\*' @DonneesRenvoyees"; \& \& sub sub1 { return "Cinquante\-six", "toto", 2; } .Ve .PP Dans cet exemple, la me\*'thode \fIjoin()\fR retourne de\*`s que le thread se termine. En plus d'attendre qu'un thread termine et de rassembler les valeurs qu'il peut retourner, \fIjoin()\fR effectue aussi le nettoyage ne\*'cessaire. Ce nettoyage peut prendre des ressources importantes, particulie\*`rement pour les programmes qui tournent longtemps et ge\*'ne\*`rent de nombreux threads. Si vous ne voulez pas re\*'cupe\*'rer les valeurs de retour ni attendre que le thread finisse, vous devriez pluto\*^t appeler la me\*'thode \fIdetach()\fR, de\*'crite ci\-dessous. .Sh "Ignorer un thread" .IX Subsection "Ignorer un thread" \&\fIjoin()\fR fait trois choses : il attend qu'un thread se termine, le nettoie, et retourne les valeurs qu'il peut avoir produites. Mais que faire si vous n'e\*^tes pas inte\*'resse\*' par le retour du thread, et que vous n'avez cure de quand il finit ? Tout ce qui vous importe alors est que le thread soit nettoye\*' quand il se termine. .PP Dans ce cas, utilisez la me\*'thode \fIdetach()\fR. Une fois qu'un thread et de\*'tache\*', il continuera a\*` s'exe\*'cuter jusqu'a\*` ce qu'il termine, apre\*`s quoi Perl le nettoiera automatiquement. .PP .Vb 1 \& use threads; \& \& $thr = threads\->new(\e&sub1); # Ge\*'ne\*'rer le thread \& \& $thr\->detach; # A partir de maintenant, nous nous de\*'sinte\*'ressons \& # officiellement du thread \& \& sub sub1 { \& $a = 0; \& while (1) { \& $a++; \& print "\e$a vaut $a\en"; \& sleep 1; \& } \& } .Ve .PP Une fois qu'un thread est de\*'tache\*', il ne peut plus e\*^tre rejoint (avec \&\f(CW\*(C`join()\*(C'\fR), et toute valeur de retour qu'il pourrait avoir produite (s'il avait de\*'ja\*` fini et attendait un \fIjoin()\fR) est perdue. .SH "Threads et donne\*'es" .IX Header "Threads et donne'es" Maintenant que nous avons couvert les bases des threads, il est temps de passer au sujet suivant : les donne\*'es. L'utilisation des threads introduit quelques complications a\*` l'acce\*`s aux donne\*'es dont les programmes sans threads n'ont jamais a\*` se pre\*'occuper. .Sh "Donne\*'es partage\*'es et non partage\*'es" .IX Subsection "Donne'es partage'es et non partage'es" La plus grande diffe\*'rence entre les ithreads de Perl et les vieux threads 5.005, ou d'ailleurs n'importe quel autre syste\*`me de threads, est que par de\*'faut, aucune donne\*'e n'est partage\*'e. Quand un nouveau thread Perl est cre\*'e\*', toutes les donne\*'es associe\*'es au thread courant sont copie\*'es vers le nouveau thread, et sont dore\*'navant donne\*'es prive\*'es du nouveau thread ! C'est similaire dans l'esprit a\*` ce qui arrive quand un processus \s-1UNIX\s0 fait un \fIfork()\fR, sauf que dans notre cas les donne\*'es sont copie\*'es vers une partie diffe\*'rente de la me\*'moire a\*` l'inte\*'rieur du me\*^me processus, sans qu'un vrai \fIfork()\fR ait lieu. .PP Pour faire bon usage des threads, cependant, on veut ge\*'ne\*'ralement partager des donne\*'es entre eux. On peut le faire gra\*^ce au module threads::shared et a\*` l'attribut \f(CW\*(C` : shared\*(C'\fR (\fIpartage\*'\fR). .PP .Vb 2 \& use threads; \& use threads::shared; \& \& my $toto : shared = 1; \& my $tata = 1; \& threads\->new(sub { $toto++; $tata++ })\->join; \& \& print "$toto\en"; # affiche 2 car $toto est partage\*' \& print "$tata\en"; # affiche 1 car $tata n'est pas partage\*' .Ve .PP Dans le cas d'un tableau partage\*', tous les e\*'le\*'ments du tableau sont partage\*'s, et pour une table de hachage partage\*'e, toutes les cle\*'s et les valeurs sont partage\*'es. Cela place des restrictions sur ce qui peut e\*^tre affecte\*' a\*` des e\*'le\*'ments de tableaux et de tables de hachage partage\*'s : seules des valeurs simples ou des re\*'fe\*'rences a\*` des variables partage\*'es sont autorise\*'es \- de fac\*,on a\*` ce qu'une variable prive\*'e ne puisse accidentellement devenir partage\*'e. Un affectation incorrecte entrai\*^ne la mort du thread (\fIdie\fR). Par exemple : .PP .Vb 2 \& use threads; \& use threads::shared; \& \& my $var = 1; \& my $svar : shared = 2; \& my %hash : shared; \& \& ... cre\*'er quelques threads ... \& \& $hash{a} = 1; # pour tous les threads, exists($hash{a}) et $hash{a} == 1 \& $hash{a} = $var; # ok \- copie par valeur : me\*^me effet que pre\*'ce\*'demment \& $hash{a} = $svar; # ok \- copie par valeur : me\*^me effet que pre\*'ce\*'demment \& $hash{a} = \e$svar; # ok \- re\*'fe\*'rence a\*` une variable partage\*'e \& $hash{a} = \e$var; # entrai\*^ne la terminaison (I) \& delete $hash{a}; # ok \- pour tous les threads, !exists($hash{a}) .Ve .PP Remarquez qu'une variable partage\*'e garantit que si deux threads ou plus essaient de la modifier au me\*^me moment, son e\*'tat interne ne sera pas corrompu. Cependant, il n'y a pas de garantie au\-dela\*` de celle\-ci, comme cela est explique\*' dans la prochaine section. .Sh "Pie\*`ges des threads : \fIrace conditions\fP" .IX Subsection "Pie`ges des threads : race conditions" Note de traduction : nous utilisons le terme anglais \fIrace condition\fR pour de\*'signer un cas ou\*` le comportement d'un programme de\*'pend de l'ordre d'e\*'ve\*`nements particuliers, alors que cet ordre ne peut pas e\*^tre garanti. .PP Les threads apportent des outils tre\*`s utiles, mais ame\*`nent aussi leur lot de pie\*`ges. L'un de ces pie\*`ges est la race condition. .PP .Vb 2 \& use threads; \& use threads::shared; \& \& my $a : shared = 1; \& $thr1 = threads\->new(\e&sub1); \& $thr2 = threads\->new(\e&sub2); \& \& $thr1\->join; \& $thr2\->join; \& print "$a\en"; \& \& sub sub1 { my $toto = $a; $a = $toto + 1; } \& sub sub2 { my $tata = $a; $a = $tata + 1; } .Ve .PP Quelle sera la valeur de \f(CW$a\fR, d'apre\*`s vous ? Par chance, la re\*'ponse est \&\*(L"c\*,a de\*'pend\*(R". \fIsub1()\fR et \fIsub2()\fR acce\*`dent toutes les deux a\*` la variable globale \f(CW$a\fR, une fois en lecture et une fois en e\*'criture. Selon diffe\*'rents facteurs comme l'algorithme d'ordonnancement de votre imple\*'mentation des threads ou la phase de la lune, \f(CW$a\fR peut e\*^tre 2 ou 3. .PP Les race conditions ont pour cause un acce\*`s non synchronise\*' a\*` des donne\*'es partage\*'es. Sans synchronisation explicite, il n'y a aucune fac\*,on d'e\*^tre su\*^r que rien n'arrive aux donne\*'es partage\*'es entre le moment ou\*` vous les lisez et celui ou\*` vous les modifiez. Me\*^me un bout de code aussi simple que l'exemple suivant pre\*'sente un proble\*`me : .PP .Vb 8 \& use threads; \& my $a : shared = 2; \& my $b : shared; \& my $c : shared; \& my $thr1 = threads\->create(sub { $b = $a; $a = $b + 1; }); \& my $thr2 = threads\->create(sub { $c = $a; $a = $c + 1; }); \& $thr1\->join; \& $thr2\->join; .Ve .PP Deux threads acce\*`dent a\*` \f(CW$a\fR. Chacun peut potentiellement e\*^tre interrompu n'importe quand, et ils peuvent e\*^tre exe\*'cute\*'s dans n'importe quel ordre. A la fin, \f(CW$a\fR peut valoir 3 ou 4, et \f(CW$b\fR et \f(CW$c\fR peuvent chacun valoir 2 ou 3. .PP Me\*^me pour \f(CW\*(C`$a += 5\*(C'\fR et \f(CW\*(C`$a++\*(C'\fR, l'atomicite\*' n'est pas garantie. .PP A chaque fois que votre programme acce\*`de a\*` des donne\*'es ou des ressources auxquelles peuvent aussi acce\*'der d'autres threads, vous devez prendre des mesures pour assurer la coordination des acce\*`s, ou courir le risque de vous retrouver avec des donne\*'es inconsistantes ou des race conditions. Remarquez que Perl prote\*`ge son e\*'tat interne contre vos race conditions, mais il ne vous prote\*`ge pas de vous\-me\*^me. .SH "Synchronisation et contro\*^le" .IX Header "Synchronisation et contro^le" Perl fournit plusieurs me\*'canismes pour coordonner les interactions entre les threads et leurs donne\*'es, et pour e\*'viter les race conditions. Certains ressemblent aux techniques communes des bibliothe\*`ques de gestion des threads, comme \f(CW\*(C`pthreads\*(C'\fR ; d'autres sont spe\*'cifiques a\*` Perl. Le plus souvent, les techniques standards (comme les attentes de condition) sont maladroites et difficiles a\*` mettre en place correctement. Quand c'est possible, il est ge\*'ne\*'ralement plus facile d'utiliser les proce\*'de\*'s perliens comme les files d'attente, qui se chargent d'une partie du travail pe\*'nible. .Sh "Contro\*^ler l'acce\*`s : \fIlock()\fP" .IX Subsection "Contro^ler l'acce`s : lock()" La fonction \fIlock()\fR prend une variable partage\*'e et la verrouille. Aucun autre thread ne peut la verrouiller jusqu'a\*` ce que la variable ait e\*'te\*' de\*'verrouille\*'e par le thread qui a pose\*' le verrou. Le de\*'verrouillage arrive automatiquement quand le thread qui tient le verrou sort du premier bloc exte\*'rieur qui contient l'appel a\*` la fonction \&\f(CW\*(C`lock()\*(C'\fR. L'utilisation de \fIlock()\fR est simple ; l'exemple suivant fait faire des calculs en paralle\*`le a\*` plusieurs threads, en mettant a\*` jour un total courant de temps en temps : .PP .Vb 2 \& use threads; \& use threads::shared; \& \& my $total : shared = 0; \& \& sub calc { \& for (;;) { \& my $resultat; \& # (... faire des calculs et affecter a\*` $resultat ...) \& { \& lock($total); # bloque jusqu'a\*` obtenir le verrou \& $total += $resultat; \& } # le verrou est automatiquement rela\*^che\*' a\*` la sortie du bloc \& last if $resultat == 0; \& } \& } \& \& my $thr1 = threads\->new(\e&calc); \& my $thr2 = threads\->new(\e&calc); \& my $thr3 = threads\->new(\e&calc); \& $thr1\->join; \& $thr2\->join; \& $thr3\->join; \& print "total=$total\en"; .Ve .PP \&\fIlock()\fR bloque le thread jusqu'a\*` ce que la variable a\*` verrouiller soit disponible. Quand \fIlock()\fR retourne, votre thread peut e\*^tre su\*^r qu'aucun autre ne peut verrouiller la variable jusqu'a\*` la fin du premier bloc exte\*'rieur contenant l'appel a\*` \fIlock()\fR. .PP Il est important de noter que les verrous n'empe\*^chent pas l'acce\*`s a\*` la variable en question, mais seulement les tentatives de verrouillage. Cela s'inscrit dans la longue amitie\*' qu'a\*` Perl avec la programmation bien e\*'leve\*'e, ainsi que le verrouillage consultatif (\fIadvisory locking\fR) de fichiers que \fIflock()\fR permet. .PP Vous pouvez verrouiller des tableaux et des tables de hachage, aussi bien que des scalaires. Cependant, le verrouillage d'un tableau ne bloque pas d'e\*'ventuels verrouillages ulte\*'rieurs sur ses e\*'le\*'ments, mais juste ceux sur le tableau lui\-me\*^me. .PP Les verrous sont re\*'cursifs, ce qui veut dire qu'un thread peut verrouiller une variable plus d'une fois. Le verrou durera jusqu'a\*` ce que le \fIlock()\fR le plus exte\*'rieur sur la variable arrive au bout de sa porte\*'e. Par exemple : .PP .Vb 2 \& my $x : shared; \& fais(); \& \& sub fais { \& { \& { \& lock($x); # attendre le verrouillage \& lock($x); # NOOP \- nous avons de\*'ja\*` le verrou \& { \& lock($x); # NOOP \& { \& lock($x); # NOOP \& verrouille_le_encore(); \& } \& } \& } # *** de\*'verrouillage implicite ici *** \& } \& } \& \& sub verrouille_le_encore { \& lock($x); # NOOP \& } # rien n'arrive ici .Ve .PP Notez qu'il n'y a pas de fonction \fIunlock()\fR \- la seule fac\*,on de de\*'verrouiller une variable est de laisser le verrou sortir de sa porte\*'e. .PP Un verrou peut e\*^tre utilise\*' pour pre\*'server les donne\*'es contenues dans la variable verrouille\*'e, mais il peut aussi servir a\*` garder autre chose, comme une section de code. Dans ce cas, la variable verrouille\*'e ne contient pas de donne\*'es utiles, et n'existe que pour e\*^tre verrouille\*'e. La variable se comporte alors comme une mutex ou un se\*'maphore simple des bibliothe\*`ques traditionnelles de gestion des threads. .Sh "Un pie\*`ge des threads : interblocages (\fIdeadlocks\fP)" .IX Subsection "Un pie`ge des threads : interblocages (deadlocks)" Les verrous sont un outil pratique pour synchroniser les acce\*`s aux donne\*'es, et leur bon usage est la cle\*' de la su\*^rete\*' des donne\*'es partage\*'es. Malheureusement, ils ne sont pas sans danger, en particulier quand plusieurs verrous sont en jeu. Contemplez le code suivant : .PP .Vb 1 \& use threads; \& \& my $a : shared = 4; \& my $b : shared = "toto"; \& my $thr1 = threads\->new(sub { \& lock($a); \& threads\->yield; \& sleep 20; \& lock($b); \& }); \& my $thr2 = threads\->new(sub { \& lock($b); \& threads\->yield; \& sleep 20; \& lock($a); \& }); .Ve .PP Ce programme va probablement se bloquer jusqu'a\*` ce que vous le tuiez. La seule fac\*,on qu'il ne se bloque pas est qu'un des deux threads acquie\*`re les deux verrous en premier. Une version garantie de bloquer serait plus complique\*'e, mais le principe serait le me\*^me. .PP Le premier thread va acque\*'rir un verrou sur \f(CW$a\fR, puis, apre\*`s une pause durant laquelle le second thread a probablement eu le temps de travailler, essayer de verrouiller \f(CW$b\fR. Pendant ce temps, le second thread acquiert un verrou sur \f(CW$b\fR, puis plus tard essaie de verrouiller \&\f(CW$a\fR. La deuxie\*`me tentative de verrouillage pour les deux threads va bloquer, chacun attendant que l'autre rela\*^che son verrou. .PP Cette situation est appele\*'e un interblocage, et elle arrive de\*`s que deux threads ou plus essaient d'obtenir un verrou sur des ressources que les autres ont verrouille\*'es. Chaque thread va bloquer, en attendant que l'autre rela\*^che un verrou sur une ressource. Cependant, cela n'arrive jamais puisque le thread qui de\*'tient la ressource est lui\-me\*^me en train d'attendre qu'un autre verrou soit rela\*^che\*'. .PP Il y a plusieurs fac\*,on de re\*'gler ce genre de proble\*`mes. La meilleure est de s'assurer que tous les threads acquie\*`rent les verrous dans le me\*^me ordre. Si, par exemple, vous verrouillez les variables \f(CW$a\fR, \f(CW$b\fR et \&\f(CW$c\fR, verrouillez toujours \f(CW$a\fR avant \f(CW$b\fR, et \f(CW$b\fR avant \f(CW$c\fR. Il est aussi pre\*'fe\*'rable de ne garder les verrous que pendant une courte pe\*'riode de temps pour minimiser les risques d'interblocage. .PP Les autres primitives de synchronisation de\*'crites plus bas peuvent souffrir de proble\*`mes similaires. .Sh "Files d'attente (\fIqueues\fP) : transmettre des donne\*'es" .IX Subsection "Files d'attente (queues) : transmettre des donne'es" Une file d'attente est un objet spe\*'cial qui vous permet d'enfourner des donne\*'es d'un co\*^te\*' et de les re\*'cupe\*'rer de l'autre, sans avoir a\*` vous inquie\*'ter des proble\*`mes de synchronisation. C'est un animal assez simple, qui ressemble a\*` ceci : .PP .Vb 2 \& use threads; \& use Thread::Queue; \& \& my $File = Thread::Queue\->new; \& $thr = threads\->new(sub { \& while ($Element = $File\->dequeue) { \& print "Retire\*' $Element de la file\en"; \& } \& }); \& \& $File\->enqueue(12); \& $File\->enqueue("A", "B", "C"); \& $File\->enqueue(\e$thr); \& sleep 10; \& $File\->enqueue(undef); \& $thr\->join; .Ve .PP Vous pouvez cre\*'er une file d'attente avec \f(CW\*(C`new Thread::Queue\*(C'\fR. Ensuite, vous pouvez y empiler des listes de scalaires avec \fIenqueue()\fR, et en retirer des scalaires de l'autre co\*^te\*' avec \&\fIdequeue()\fR. Une file n'a pas de taille fixe, et peut grandir a\*` volonte\*' pour loger tout ce qui y est pousse\*'. .PP Si une file est vide, \fIdequeue()\fR bloque jusqu'a\*` ce qu'un autre thread y pousse quelque chose. Cela fait des files l'e\*'le\*'ment ide\*'al pour les boucles d'e\*'ve\*`nements et les autres sortes de communication entre threads. .Sh "Se\*'maphores : synchroniser les acce\*`s aux donne\*'es" .IX Subsection "Se'maphores : synchroniser les acce`s aux donne'es" Les se\*'maphores sont un genre de me\*'canisme de verrouillage ge\*'ne\*'rique. Dans leur forme la plus basique, ils se comportent tout a\*` fait comme des scalaires munis d'un verrou, sauf qu'ils ne peuvent pas contenir de donne\*'es, et doivent e\*^tre explicitement de\*'verrouille\*'s. Dans leur forme avance\*'e, ils agissent comme un genre de compteur, et peuvent autoriser plusieurs threads a\*` de\*'tenir le \*(L"verrou\*(R" a\*` un instant donne\*'. .Sh "Se\*'maphores de base" .IX Subsection "Se'maphores de base" Les se\*'maphores ont deux me\*'thodes, \fIdown()\fR et \fIup()\fR : \fIdown()\fR de\*'cre\*'mente le compteur de ressource, \fIup()\fR l'incre\*'mente. Les appels a\*` \fIdown()\fR bloqueront si le compteur courant du se\*'maphore devait descendre sous ze\*'ro pour effectuer le \fIdown()\fR. Ce programme en donne une rapide de\*'monstration : .PP .Vb 2 \& use threads qw(yield); \& use Thread::Semaphore; \& \& my $semaphore = new Thread::Semaphore; \& my $VariableGlobale : shared = 0; \& \& $thr1 = new threads \e&sub_exemple, 1; \& $thr2 = new threads \e&sub_exemple, 2; \& $thr3 = new threads \e&sub_exemple, 3; \& \& sub sub_exemple { \& my $NoSub = shift @_; \& my $NbTentatives = 10; \& my $CopieLocale; \& sleep 1; \& while ($NbTentatives\-\-) { \& $semaphore\->down; \& $CopieLocale = $VariableGlobale; \& print "$NbTentatives essais restants pour la sub" \& . " $NoSub (\e$VariableGlobale est $VariableGlobale)\en"; \& yield; \& sleep 2; \& $CopieLocale++; \& $VariableGlobale = $CopieLocale; \& $semaphore\->up; \& } \& } \& \& $thr1\->join; \& $thr2\->join; \& $thr3\->join; .Ve .PP Les trois appels de la fonction ope\*`rent tous en me\*^me temps. Le se\*'maphore, cependant, s'assure qu'il n'y a qu'un thread a\*` la fois qui acce\*`de a\*` la variable globale. .Sh "Se\*'maphores avance\*'s" .IX Subsection "Se'maphores avance's" Par de\*'faut, les se\*'maphores se comportent comme des verrous, et laissent un seul thread a\*` la fois de\*'cre\*'menter leur compteur avec \&\fIdown()\fR. On peut cependant en faire d'autres usages. .PP Chaque se\*'maphore a un compteur attache\*'. Par de\*'faut, les se\*'maphores sont cre\*'e\*'s avec un compteur mis a\*` un, \fIdown()\fR de\*'cre\*'mente ce compteur d'une unite\*', et \fIup()\fR l'incre\*'mente d'une unite\*'. Nous pouvons ne\*'anmoins utiliser d'autres valeurs de la fac\*,on suivante : .PP .Vb 5 \& use threads; \& use Thread::Semaphore; \& my $semaphore = Thread::Semaphore\->new(5); \& # Cre\*'e un se\*'maphore avec le compteur initialise\*' \& # a\*` cinq \& \& $thr1 = threads\->new(\e&sub1); \& $thr2 = threads\->new(\e&sub1); \& \& sub sub1 { \& $semaphore\->down(5); # De\*'cre\*'mente le compteur de cinq unite\*'s \& # Faire des choses ici \& $semaphore\->up(5); # Incre\*'mente le compteur de cinq unite\*'s \& } \& \& $thr1\->detach; \& $thr2\->detach; .Ve .PP Si \fIdown()\fR essaie de de\*'cre\*'menter le compteur au-dessous de ze\*'ro, il bloque jusqu'a\*` ce que le compteur devienne assez grand. Notez que bien qu'un se\*'maphore puisse e\*^tre cre\*'e\*' avec un compteur initial de ze\*'ro, tout \fIup()\fR ou \fIdown()\fR change toujours le compteur d'au moins une unite\*' ; par conse\*'quent, \f(CW$semaphore\fR\->\fIdown\fR\|(0) est identique a\*` \&\f(CW$semaphore\fR\->\fIdown\fR\|(1). .PP La question, bien su\*^r, est pourquoi voudrait-on faire c\*,a ? Pourquoi cre\*'er un se\*'maphore avec un compteur initial qui n'est pas e\*'gal a\*` un, ou pourquoi de\*'cre\*'menter/incre\*'menter par plus d'une unite\*' ? La re\*'ponse tient dans la disponibilite\*' des ressources. De nombreuses ressources auxquelles on veut ge\*'rer l'acce\*`s peuvent e\*^tre utilise\*'es par plusieurs threads en me\*^me temps en toute se\*'curite\*'. .PP Par exemple, conside\*'rons un programme centre\*' autour d'une interface graphique. Il utilise un se\*'maphore pour synchroniser l'acce\*`s a\*` l'affichage, de manie\*`re a\*` ce qu'un seul thread a\*` la fois puisse dessiner. C'est pratique, mais bien su\*^r vous ne voulez pas qu'un thread commence a\*` dessiner avant que tout ne soit mis en place. Dans ce cas, vous pouvez cre\*'er un se\*'maphore avec un compteur initial de ze\*'ro, et l'incre\*'menter quand tout est pre\*^t pour le dessin. .PP Les se\*'maphores avec un compteur plus grand que un sont utiles pour e\*'tablir des quotas. Supposons, par exemple, que vous avez plusieurs threads qui peuvent faire des E/S en paralle\*`le. Vous ne voulez pas que tous lisent et e\*'crivent en me\*^me temps, car cela pourrait encombrer vos canaux d'E/S, ou e\*'puiser le quota de descripteurs de fichiers de votre processus. Vous pouvez utiliser un se\*'maphore initialise\*' au nombre de reque\*^tes d'E/S (ou d'ouvertures de fichiers) concurrentes que vous voulez autoriser a\*` un instant donne\*', et laisser tranquillement vos threads se bloquer et se de\*'bloquer les uns les autres. .PP Les incre\*'ments et de\*'cre\*'ments plus grands que un sont utiles quand un thread doit libe\*'rer ou acce\*'der a\*` plusieurs ressources a\*` la fois. .Sh "\fIcond_wait()\fP et \fIcond_signal()\fP" .IX Subsection "cond_wait() et cond_signal()" Ces deux fonctions peuvent e\*^tre utilise\*'es en conjonction avec les verrous pour notifier des threads qui coope\*`rent qu'une ressource est devenue disponible. Elles sont tre\*`s semblables aux fonctions qu'on peut trouver dans \f(CW\*(C`pthreads\*(C'\fR. Mais dans la plupart des cas, les files d'attente sont plus simples a\*` utiliser et plus intuitives. Voyez threads::shared pour plus de de\*'tails. .SH "Fonctions utiles ge\*'ne\*'rales" .IX Header "Fonctions utiles ge'ne'rales" Nous avons couvert les parties principales du syste\*`me de gestion des threads de Perl, et avec ces outils vous devriez e\*^tre bien e\*'quipe\*' pour e\*'crire du code et des modules utilisant les threads. Mais il reste quelques petits e\*'le\*'ments que nous n'avons pas aborde\*'s. .Sh "Dans quel thread suis-je ?" .IX Subsection "Dans quel thread suis-je ?" La me\*'thode de classe \f(CW\*(C`threads\->self\*(C'\fR fournit a\*` votre programme une fac\*,on d'obtenir un objet qui repre\*'sente le thread dans lequel il s'exe\*'cute. Vous pouvez utiliser cet objet de la me\*^me fac\*,on que ceux renvoye\*'s par les fonctions de cre\*'ation de threads. .Sh "Identificateur de thread" .IX Subsection "Identificateur de thread" \&\fItid()\fR est une me\*'thode d'objet sur les threads qui renvoie l'identificateur du thread que l'objet repre\*'sente. Les identificateurs sont des entiers, et celui du thread principal d'un programme est 0. Actuellement, Perl affecte un tid unique a\*` chaque thread cre\*'e\*' dans votre programme ; le premier thread cre\*'e rec\*,oit un tid de 1, puis chaque nouveau thread rec\*,oit un tid incre\*'mente\*' de 1. .Sh "Est-ce que ces threads sont les me\*^mes ?" .IX Subsection "Est-ce que ces threads sont les me^mes ?" La me\*'thode \fIequal()\fR prend deux objets thread et renvoie vrai si les objets repre\*'sentent le me\*^me thread, faux sinon. .PP La comparaison avec == est surcharge\*'e pour les objets threads, et vous pouvez donc comparer ceux-ci comme vous le faites pour les objets ordinaires. .Sh "Quels threads sont en train de tourner ?" .IX Subsection "Quels threads sont en train de tourner ?" \&\f(CW\*(C`threads\->list\*(C'\fR renvoie une liste d'objets threads, un pour chaque thread actuellement en train de tourner et non de\*'tache\*'. C'est utile a\*` plusieurs fins, dont le nettoyage a\*` la fin de votre programme : .PP .Vb 7 \& # Parcourir la liste de tous les threads \& foreach $thr (threads\->list) { \& # Ne pas rejoindre le thread principal ni nous\-me\*^mes \& if ($thr\->tid && !threads::equal($thr, threads\->self)) { \& $thr\->join; \& } \& } .Ve .PP Si certains threads n'ont pas fini de tourner quand le thread Perl principal se termine, Perl vous avertit et meurt, puisqu'il est impossible a\*` Perl de sortir proprement alors que d'autres threads tournent encore. .SH "Un exemple complet" .IX Header "Un exemple complet" De\*'ja\*` embrouille\*' ? Il est temps qu'un exemple illustre une partie de ce que nous avons expose\*'. Ce programme trouve des nombres premiers en utilisant des threads. .PP .Vb 10 \& 1 #!/usr/bin/perl \-w \& 2 # prime\-pthread, avec l'aimable autorisation de Tom Christiansen \& 3 \& 4 use strict; \& 5 \& 6 use threads; \& 7 use Thread::Queue; \& 8 \& 9 my $flot = new Thread::Queue; \& 10 my $enfant = new threads(\e&teste_nombre, $flot, 2); \& 11 \& 12 for my $i ( 3 .. 1000 ) { \& 13 $flot\->enqueue($i); \& 14 } \& 15 \& 16 $flot\->enqueue(undef); \& 17 $enfant\->join; \& 18 \& 19 sub teste_nombre { \& 20 my ($amont, $premier_courant) = @_; \& 21 my $enfant; \& 22 my $aval = new Thread::Queue; \& 23 while (my $nombre = $amont\->dequeue) { \& 24 next unless $nombre % $premier_courant; \& 25 if ($enfant) { \& 26 $aval\->enqueue($nombre); \& 27 } else { \& 28 print "Ai trouve\*' le premier $nombre\en"; \& 29 $enfant = new threads(\e&teste_nombre, $aval, $nombre); \& 30 } \& 31 } \& 32 $aval\->enqueue(undef) if $enfant; \& 33 $enfant\->join if $enfant; \& 34 } .Ve .PP Ce programme utilise le mode\*`le de travail a\*` la chai\*^ne pour ge\*'ne\*'rer des nombres premiers. Chaque thread dans la chai\*^ne a une file d'entre\*'e qui fournit des nombres a\*` tester, un nombre premier dont il est responsable, et une file de sortie dans laquelle il enfile les nombres qui ont rate\*' son test (de non primaute\*'). Si le thread tombe sur un nombre qui a rate\*' le test et qu'il n'y a pas de thread enfant, c'est qu'il doit avoir trouve\*' un nouveau nombre premier. Dans ce cas, un nouveau thread enfant est cre\*'e\*' pour ce premier, et accole\*' au bout de la chai\*^ne. .PP Cela sonne probablement un peu plus difficile que cela ne l'est vraiment ; passons donc en revue ce programme morceau par morceau, et examinons ce qu'il fait. (Pour ceux qui essaient de se rappeler ce qu'est au juste un nombre premier, c'est un nombre qui est seulement divisible par 1 et lui\-me\*^me.) .PP Le gros du travail est fait par la fonction teste_nombre, qui prend une re\*'fe\*'rence a\*` sa file d'entre\*'e et un nombre premier dont elle est responsable. Apre\*`s avoir extrait la file et le nombre premier pour lequel elle fait les tests (ligne 20), nous cre\*'ons une nouvelle file (ligne 22) et nous re\*'servons un scalaire pour le thread que nous allons sans doute cre\*'er plus loin (ligne 21). .PP La boucle while de la ligne 23 a\*` la ligne 31 retire un scalaire de la file d'entre\*'e et le teste avec le nombre premier dont le thread est responsable. La ligne 24 de\*'termine s'il y a un reste quand nous calculons le nombre a\*` tester modulo notre premier. S'il y en a un, le nombre n'est pas divisible par notre premier, et nous devons donc soit le passer au thread suivant si nous en avons cre\*'e\*' un (ligne 26) soit cre\*'er un nouveau thread si ce n'est pas encore fait. .PP La cre\*'ation du nouveau thread se trouve ligne 29. Nous passons une re\*'fe\*'rence a\*` la file que nous avons cre\*'e\*'e et le nombre premier que nous avons trouve\*'. .PP Enfin, une fois que la boucle se termine (parce que nous avons trouve\*' un 0 ou un undef dans la file, qui sert comme un ordre de terminer), nous notifions notre enfant et attendons qu'il sorte si nous en avons cre\*'e\*' un (lignes 32 et 37). .PP Pendant ce temps, dans le thread principal, nous cre\*'ons une file (ligne 9) et le thread enfant initial (ligne 10), et nous lui passons le premier nombre premier : 2. Ensuite, nous enfilons tous les nombres de 3 a\*` 1000 pour qu'il soient teste\*'s (lignes 12\-14), puis une notification de terminer (ligne 16), et nous attendons que le premier thread enfant finisse (ligne 17). Nous savons que tout va bien quand nous retournons du \fIjoin()\fR, puisqu'un enfant ne mourra pas avant que ses propres enfants ne soient morts. .PP Voila tout. C'est tre\*`s simple ; comme beaucoup de programmes Perl, l'explication est bien plus longue que le programme. .SH "Conside\*'rations de performance" .IX Header "Conside'rations de performance" La chose la plus importante a\*` garder a\*` l'esprit quand on compare les ithreads a\*` d'autres mode\*`les de gestion des threads est le fait que pour chaque thread cre\*'e\*', une copie comple\*`te de toutes les variables et donne\*'es du thread parent doive e\*^tre faite. La cre\*'ation d'un thread peut donc e\*^tre cou\*^teuse en termes de me\*'moire autant que de temps de calcul. La meilleure fac\*,on de re\*'duire ces cou\*^ts est d'avoir un faible nombre de threads qui vivent longtemps, tous cre\*'e\*'s assez to\*^t \- avant que le thread de base n'ait accumule\*' trop de donne\*'es en me\*'moire. Bien su\*^r, apre\*`s qu'un thread a e\*'te\*' cre\*'e\*', ses performances et son occupation me\*'moire devraient e\*^tre peu diffe\*'rente que celles de code ordinaire. .PP Notez aussi que dans l'imple\*'mentation actuelle, les variables partage\*'es prennent un peu plus de me\*'moire et sont le\*'ge\*`rement plus lentes que les variables ordinaires. .SH "Changements au niveau du processus" .IX Header "Changements au niveau du processus" Bien que les threads aient des chemins d'exe\*'cution se\*'pare\*'s et que les donne\*'es Perl soient prive\*'es a\*` chaque thread a\*` moins qu'elles ne soient explicitement partage\*'es, les threads peuvent modifier l'e\*'tat du processus complet, affectant ainsi les autres threads. .PP L'exemple e\*'vident de cela est le changement du re\*'pertoire de travail courant avec \fIchdir()\fR. Si un thread appelle \fIchdir()\fR, le re\*'pertoire de travail de tous les threads change. .PP Un exemple encore plus spectaculaire d'un changement qui intervient au niveau du processus est \fIchroot()\fR : le re\*'pertoire racine de tous les threads change alors, et aucun thread ne peut revenir en arrie\*`re (par opposition a\*` \fIchdir()\fR). .PP \&\fIumask()\fR et les changements d'uid et de gid sont d'autres exemples de changement qui ont lieu au niveau du processus. .PP Si jamais l'envie vous prenait de me\*'langer \fIfork()\fR et les threads, allongez-vous et attendez que c\*,a passe. Mais si vous voulez vraiment savoir, la se\*'mantique est que \fIfork()\fR duplique tous les threads (sous \s-1UNIX\s0 du moins \- les autres plateformes feront quelque chose de diffe\*'rent). .PP De me\*^me, vous ne devriez pas essayer de me\*'langer les signaux et les threads. Les imple\*'mentations de\*'pendent de la plateforme, et me\*^me la se\*'mantique \s-1POSIX\s0 pourrait vous surprendre (sans compter que Perl ne vous fournit pas l'\s-1API\s0 \s-1POSIX\s0 comple\*`te). .SH "Re\*'entrance des bibliothe\*`ques syste\*`me" .IX Header "Re'entrance des bibliothe`ques syste`me" La re\*'entrance des diffe\*'rents appels syste\*`me est hors du contro\*^le de Perl. Parmi les appels connus pour ne pas e\*^tre re\*'entrants, on trouve : \&\fIlocaltime()\fR, \fIgmtime()\fR, get{gr,host,net,proto,serv,pw}*(), \fIreaddir()\fR, \&\fIrand()\fR, et \fIsrand()\fR, et en ge\*'ne\*'ral, tous les appels qui de\*'pendent d'un e\*'tat externe global. .PP Si le syste\*`me sur lequel perl est compile\*' a des versions re\*'entrantes de ces fonctions, elles seront utilise\*'es. A part c\*,a, Perl est a\*` la merci de la re\*'entrance ou de la non\-re\*'entrance de ces fonctions. Consultez la documentation de votre bibliothe\*`que C pour plus de de\*'tails. .PP Sur certaines plateforme, l'interface re\*'entrante peut e\*'chouer si le tampon de re\*'sultat est trop petit (par exemple \fIgetgrent()\fR peut renvoyer d'assez volumineuses listes de membres). Perl re\*'essaie alors un certain nombre de fois en agrandissant le tampon, mais jusqu'a\*` 64ko seulement (pour des raisons de se\*'curite\*'). .SH "Conclusion" .IX Header "Conclusion" Un tutoriel complet sur les threads pourrait remplir un livre entier (en fait, de nombreux livres ont pour seul objet ce type de tutoriel), mais avec ce que nous avons couvert dans cette introduction, vous devriez e\*^tre en bonne voie pour devenir un expert de la programmation avec les threads en Perl. .SH "Bibliographie" .IX Header "Bibliographie" Voici une courte bibliographie, aimablement fournie par Ju\*:rgen Christoffel. .Sh "Textes introductifs" .IX Subsection "Textes introductifs" Birrell, Andrew D. An Introduction to Programming with Threads. Digital Equipment Corporation, 1989, DEC-SRC Research Report #35 en ligne sur http://gatekeeper.dec.com/pub/DEC/SRC/research\-reports/abstracts/src\-rr\-035.html (hautement recommande\*') .PP Robbins, Kay. A., et Steven Robbins. Practical Unix Programming: A Guide to Concurrency, Communication, and Multithreading. Prentice\-Hall, 1996. .PP Lewis, Bill, et Daniel J. Berg. Multithreaded Programming with Pthreads. Prentice Hall, 1997, \s-1ISBN\s0 0\-13\-443698\-9 (une introduction aux threads tre\*`s bien e\*'crite). .PP Nelson, Greg (sous la direction de). Systems Programming with Modula\-3. Prentice Hall, 1991, \s-1ISBN\s0 0\-13\-590464\-1. .PP Nichols, Bradford, Dick Buttlar, et Jacqueline Proulx Farrell. Pthreads Programming. O'Reilly & Associates, 1996, \s-1ISBN\s0 156592\-115\-1 (couvre les threads \s-1POSIX\s0). .Sh "Re\*'fe\*'rences lie\*'es aux syste\*`mes d'exploitation" .IX Subsection "Re'fe'rences lie'es aux syste`mes d'exploitation" Boykin, Joseph, David Kirschen, Alan Langerman, et Susan LoVerso. Programming under Mach. Addison\-Wesley, 1994, \s-1ISBN\s0 0\-201\-52739\-1. .PP Tanenbaum, Andrew S. Distributed Operating Systems. Prentice Hall, 1995, \s-1ISBN\s0 0\-13\-219908\-4 (tre\*`s bon manuel). .PP Silberschatz, Abraham, et Peter B. Galvin. Operating System Concepts, 4th ed. Addison\-Wesley, 1995, \s-1ISBN\s0 0\-201\-59292\-4 .Sh "Autres re\*'fe\*'rences" .IX Subsection "Autres re'fe'rences" Arnold, Ken et James Gosling. The Java Programming Language, 2nd ed. Addison\-Wesley, 1998, \s-1ISBN\s0 0\-201\-31006\-6. .PP \&\s-1FAQ\s0 de comp.programming.threads, http://www.serpentine.com/~bos/threads\-faq/ .PP Le Sergent, T. et B. Berthomieu. \*(L"Incremental MultiThreaded Garbage Collection on Virtually Shared Memory Architectures\*(R" in Memory Management : Proc. of the International Workshop \s-1IWMM\s0 92, St. Malo, France, September 1992, Yves Bekkers and Jacques Cohen, eds. Springer, 1992, \s-1ISBN\s0 3540\-55940\-X (applications pratiques des threads). .PP Arthur Bergman, \*(L"Where Wizards Fear To Tread\*(R", June 11, 2002, http://www.perl.com/pub/a/2002/06/11/threads.html .SH "Cre\*'dits" .IX Header "Cre'dits" Merci (sans ordre particulier) a\*` Chaim Frenkel, Steve Fink, Gurusamy Sarathy, Ilya Zakharevich, Benjamin Sugars, Ju\*:rgen Christoffel, Joshua Pritikin, et Alan Burlison, pour leur aide dans la ve\*'rification et la mise au point de cet article. Un grand merci a\*` Tom Christiansen pour sa re\*'e\*'criture du ge\*'ne\*'rateur de nombres premiers. .SH "AUTEUR" .IX Header "AUTEUR" Dan Sugalski (dan@sidhe.org). .PP Le\*'ge\*`rement modifie\*' par Arthur Bergman pour ajuster l'article au nouveau mode\*`le de gestion des threads. .PP Le\*'ge\*`rement retravaille\*' par Jo\*:rg Walter pour e\*^tre plus concis sur le sujet de la re\*'entrance du code Perl. .SH "Copyrights" .IX Header "Copyrights" The original version of this article originally appeared in The Perl Journal #10, and is copyright 1998 The Perl Journal ( http://www.tpj.com ). It appears courtesy of Jon Orwant and The Perl Journal. This document may be distributed under the same terms as Perl itself. .SH "Copyright" .IX Header "Copyright" La version originale de cet article est parue dans le nume\*'ro 10 de The Perl Journal, et est copyright 1998 The Perl Journal ( http://www.tpj.com ). Il est utilise\*' ici avec l'aimable autorisation de Jon Orwant et de The Perl Journal. Ce document peut e\*^tre redistribue\*' sous les me\*^me termes que Perl lui\-me\*^me. .PP Pour plus d'informations, voyez threads et threads::shared. .SH "TRADUCTION" .IX Header "TRADUCTION" .Sh "Version" .IX Subsection "Version" Cette traduction franc\*,aise correspond a\*` la version anglaise distribue\*'e avec perl 5.8.0. Pour en savoir plus concernant ces traductions, consultez . .Sh "Traducteur" .IX Subsection "Traducteur" Ronan Le Hy . .Sh "Relecture" .IX Subsection "Relecture" Personne pour l'instant.