![]() |
Programmation en language LUA |
Unité d'éxécution (Threads) |
La norme ANSI C, la norme à laquelle Lua se conforme, n'a aucun mécanisme pour contrôler les fils multiples de l'exécution. Des fils et les objets de synchronisation employés pour les commander sont fournis par le logiciel d'exploitation fondamental. Vous devrez employer ces derniers afin de mettre en application le filetage dans Lua. Vous n'aurez pas besoin de modifier la distribution de Lua pour faire ceci.
Les fils sont rusés pour devenir exacts même dans le plus simple des circonstances telles qu'une application pure de C. Une application qui enfonce ou prolonge Lua doit faire face à la complexité additionnelle des fils coordonnés avec la bibliothèque de Lua. Si vos besoins de traitement multitâche peuvent être rencontrés les coroutines simple-filetés de Lua, vous bien-seriez conseillé de choisir cet itinéraire. Lisez CoroutinesTutorial pour plus de détails. Si vous choisissez de mettre en application les fils de préemption multiples à votre Lua projettent, les directives suivantes peuvent aider.
Chaque fil en C qui agit l'un sur l'autre avec Lua aura
besoin de son propre état de Lua. Chacun de ces états a sa
propre pile d'exécution. Quand un nouveau fil de C est
commencé, vous pouvez créer son état de Lua dans une de deux
manières. L'one-way doit appeler lua_open
. Ceci crée
un nouvel état qui est indépendant des états en d'autres fils.
Dans ce cas-ci, vous devrez initialiser l'état de Lua (par
exemple, bibliothèques de programmes) comme si c'était l'état d'un
nouveau programme. Cette approche élimine le besoin de serrures
de mutex (discutées ci-dessous), mais gardera les fils de partager
des données globales.
L'autre approche est d'appeler lua_newthread
. Ceci
crée un état d'enfant qui a sa propre pile et qui a accès aux
données globales. Cette approche est discutée ici.
Votre plus grand souci quand travailler avec des fils doit les empêcher de corrompre les environnements de chacun. Les problèmes subtiles ou non-ainsi-subtiles, immédiats ou parfois de façon exaspérante retardés, attendent un fil qui revient après le droit de préemption à un environnement qui a été laissé dans un état inattendu par un autre fil. Cependant, vous voulez généralement que les fils partagent certaines structures de données, aussi. C'est où les objets de mutex du logiciel d'exploitation héritent le jeu. Un objet de ce type peut être fermé à clef par pas plus d'un fil à la fois.
Avec de l'aide de vous, Lua empêchera ses structures de
données internes d'être corrompue. Quand Lua entre dans une
opération qui ne doit pas être appropriée, elle
appelle lua_lock
. Quand l'opération critique est complète,
elle appelle lua_unlock
. Dans la distribution de défaut ces
deux fonctions ne font rien. Quand employer filète dans Lua,
elles devraient être remplacées avec des réalisations
OS-DÉPENDANTES. Dans un environnement de POSIX vous emploierez
un objet de type pthread_mutex_t
. Dans Windows, vous emploierez
l'un ou l'autre une poignée dont est retourné CreateMutex
, ou, plus
de façon optimale, une structure de données opaque de
type CRITICAL_SECTION
.
Tous les coroutines dans un univers particulier de lua
doivent partager le même mutex. Évitez l'erreur d'associer le
mutex à un état spécifique de Lua et puis ne la trouvent pas encore
quand un coroutine différent dans le même univers est verrouillé.
Un exemple simple pour Win32 suit. Le dossier d'en-tête luauser.h
contient :
# définissez le lua_lock(L) LuaLock(L) # définissent le lua_unlock(L) LuaUnlock(L) # définissent le lua_userstateopen(L) LuaLockInitial(L) # définissent le lua_userstatethread(L, L1) LuaLockInitial(L1)// Lua 5.1 LuaLockInitial(lua_State vide * L) ; videz LuaLockFinal(lua_State * L) ; videz LuaLock(lua_State * L) ; videz LuaUnlock(lua_State * L) ;
Les trois définitions de préprocesseur seront employées quand Lua est compilé. En date de la version 5.0.2, Lua regrettablement ne fournit pas un appel pour détruire une serrure.
La fonction lua_userstateopen
s'appellera toutes les fois qu'un
nouvel état de Lua est créé, à si avec un appel lua_open
ou lua_newthread
. Il est important que le mutex soit
créé seulement la première fois lua_userstateopen
s'appelle.
Dans Lua 5.1 luai_userstatethread(L,L1)
est réclamés des fils créés
avec lua_newthread
. et luai_userstateopen(L)
est réclamé des états de
lua créés près lua_newstate
(mais pas près lua_newthread
).
luai_userstateclose(L)
est réclamé des fils fermés près lua_close
seulement.
Le dossier associé de C luauser.c
, contient :
# incluez < windows.h > # incluent "lua.h" # incluent le struct statique de "luauser.h" {CRITICAL_SECTION LockSct ; BOOL Init ; } Gl ; LuaLockInitial(lua_State vide * L) {si (! Gl.Ilente) {/* créez * de mutex/InitializeCriticalSection(&Gl.LockSct) ; Gl.Ilente = RECTIFIENT ; }} LuaLockFinal(lua_State vide * L)/* non appelé par Lua. */{/* détruisez un mutex. */si (Gl.Ilente) {DeleteCriticalSection(&Gl.LockSct) ; Gl.Ilente = FAUX ; }} LuaLock(lua_State vide * L) {/* attente la commande du * de mutex/EnterCriticalSection(&Gl.LockSct) ; } LuaUnlock(lua_State vide * L) {/* contrôle de la diffusion du * de mutex/LeaveCriticalSection(&Gl.LockSct) ; }
Ces deux dossiers n'ont pas besoin de résider
dans l'arbre de distribution de Lua, mais ils doivent être
accessibles pendant la construction. En plus, vous devrez
définir LUA_USER_H
de sorte que votre incluiez le dossier soit
employé par Lua. Les citations doivent faire partie de la
définition, de sorte qu'une expression quelque chose aiment
/DLUA_USER_H="""luauser.h" ""
devra être transporté au compilateur. En outre, vous pouvez devoir prolonger le chemin d'inclusion de sorte que le compilateur puisse trouver votre dossier.
Lua empêche ses structures de données internes de devenir corrompues au moyen des fonctions de fermeture. Il appartient à l'application pour empêcher des problèmes avec les structures de données exposées, s'ils sont globaux ou des upvalues. Un mutex peut être employé pour coordonner l'utilisation de ressource employant des fonctions comme celles montrent en haut. Cependant, soyez sûr d'employer un mutex différent que celui employé par Lua afin d'éviter des impasses potentielles.
En concevant des applications multi-filetées, il est utile de prêter une attention particulière à où chaque fil attend. Rendiez-vous toujours compte que des opérations sans surveillance peuvent être interrompues. Si n'importe quel autre fil pourrait être compromis par l'état dans lequel une interruption se produit, une certaine forme d'exclusion mutuelle s'appelle pour.
La méthode ci-dessus pour mettre en application le filetage dans Lua employant un mutex global est inefficace sur des systèmes de multiprocesseur. Car un fil tient le mutex global, d'autres fils l'attendent. De ce fait seulement un fil de Lua peut fonctionner à la fois, indépendamment du nombre de processeurs dans le système.
Sur quelques systèmes il pourrait être nécessaire de
rapporter le fil après avoir ouvert le mutex, afin d'empêcher le
même fil de le fermer à clef encore, au cas où il y aurait d'autres
qui attendent là-dessus. Ceci se produit au moins sur Linux en
utilisant Boost.Threads. Le dépassement luai_threadyield
(qui par
des appels de défaut lua_unlock
, suivis immédiatement
près lua_lock
) pour rapporter le fil inbetween la serrure et
l'ouvrir est probablement une bonne idée. Cependant, le
luai_threadyield s'appelle par le macro de dojump dans la machine
virtuelle, par conséquent un rendement à chaque appel de
luai_threadyield pourrait considérablement dégrader l'exécution.
L'alternative suivante pourrait être utile :
lua_State vide de luai_threadyield(struct * L) {count=0 interne statique ; y=false de bool ; si (compte -- <=0) {y=true ; count=30 ; } ; //valeurs d'essai différentes au lieu du lua_unlock(L) 30. ; si (y) thread::yield() ; lua_lock(L) ; }
![]() |
|