[Tutorial] La programmation multi-thread avec Tcl
#1
Bonjour à tous,

J'aimerais vous présenter dans ce tutorial la programmation multi-thread avec Tcl. Avant tout je vais vous expliquer brièvement ce que c'est, et quand est son intérêt.

Introduction

Tout dabord, beaucoup d'entre vous ont actuellement des processeurs multi-coeur, 2, 4, voir 8 coeurs, mais concrètement à quoi ça sert? Avoir un processeur multi-coeur permet de faire plusieurs choses en même temps.

Le soucis, c'est que souvent les gens pensent qu'avoir un processeur multi coeur suffit pour accélérer les applications, ce qui est faux. En fait c'est votre système d'exploitation qui répartit les charges, par exemple, vous ouvrez Word et Excel en même temps. Avec un processeur double coeur, Excel utilisera un processeur, et Word l'autre.

Vous avez donc un gain de temps général, mais au final l'application en elle même ne fonctionne pas plus vite, puisqu'elle n'utilise qu'un seul processeur.

(Note: Ce sont des exemples fictif pour la compréhension générale.)

C'est donc à vous, développeurs, de faire en sorte que votre application utilise tous les processeurs disponibles, et pour cela, on va utiliser les Threads. Il y a un package Tcl qui nécessite l'installation d'une bibliothèque, sous n'importe qu'elle debian/ubuntu récente, un simple apt-get install tclthread fera l'affaire.

L'idée va donc être la suivante, dans notre programme, on définira de façon explicite des threads, qui seront gérés par le système. Si on est sur un système avec un seul coeur, ça ne changera pas forcément grand chose en terme de performance, mais sur un système à plusieurs coeurs, l'OS s'arrangera pour répartir chaque Thread sur des processeurs différents, on va donc pouvoir exécuter plusieurs script Tcl en parallèle!

Exemple

Exemple de code (note: ce script est fait pour être exécuté en console, pas sur un eggdrop, afin de facilité les tests):

La documentation du package Thread est disponible à cette adresse:

http://tcl.cvs.sourceforge.net/*checkout...hread.html

tcl
# On inclue la bibliothèque des Threads
package require Thread
 
set test "ok"

# On définit un premier Thread, il est important de comprendre que chaque Thread
# est éxécuté dans un interpréteur différent! Par conséquent ils ne peuvent pas
# partager de donnée directement en mémoire.
 
set Thread1 [thread::create -joinable -preserved {
    # Ici nous définissons le code exécuté dans notre script.
    proc exemple { {max 10} } {
        for {set i 0} {$i <= $max} {incr i} {
            puts "Thread 1: $i"
        }
    }
 
    thread::wait
 
}]

# On définit un deuxième Thread, de la même façon
set Thread2 [thread::create -joinable -preserved {
    # Ici nous définissons le code exécuté dans notre script.
    proc exemple { {max 10} } {
        for {set i 0} {$i <= $max} {incr i} {
            puts "Thread 2: $i"
        }
    }
 
    thread::wait
 
}]

# Nos deux Threads ont étés créés, ils sont chacun dans une sorte de
# boucle infinies, en attendant qu'on leurs demandent de faire quelque chose.
# On va donc leur "envoyer" une commande à éxécuter:
 
thread::send -async $Thread1 [list exemple 5]
thread::send -async $Thread2 [list exemple 10]

# Cette commande fais en sorte qu'on demande au Thread de s'arreter, et l'option
# -wait indique que on attend qu'il soit effectivement arreté.
#
# ATTENTION: Ceci ne fonctione QUE si le Thread a été créé avec -joinable et
# -preserved. Sinon vous allez avoir des gros crashs!
#
# Lisez la doc pour plus d'infos ou demandez-moi.
thread::release -wait $Thread1
thread::release -wait $Thread2

# Une fois que nos deux Threads ont finis de s'éxécuter, le script s'arrete.



Et voici la sortie de ce script:

Citation :thibaut@home-up:~/Bureau$ tclsh8.5 thread.tcl
Thread 2: 0
Thread 2: 1
Thread 2: 2
Thread 2: 3
Thread 1: 0
Thread 2: 4
Thread 1: 1
Thread 2: 5
Thread 1: 2
Thread 2: 6
Thread 1: 3
Thread 2: 7
Thread 1: 4
Thread 2: 8
Thread 1: 5
Thread 2: 9
Thread 2: 10

Comme vous pouvez le constater, les deux actions sont effectués simaltanément, et c'est d'ailleurs impossible de prévoir quel sera la sortie. Si vous lancez le script plusieurs fois de suite vous obtiendrez des résultats différent.

On voit donc que l'on est capable d'effectuer deux actions en même temps. Si vous voulez encore mieux comprendre ceci, lancez un boucle infinei dans chaque Thread (while {1} {}), et regardez l'utilisation de vos processeurs (pour ceux qui ont un processeur double coeur). Vous verrez alors que vos DEUX processeurs tournent à 100%!

Conclusion et idées

Ceci n'est vraiment qu'une petite introduction aux Threads, qui sont réellement complexes à utiliser, même si avec Tcl ça parait simple, en réalité c'est beaucouo plus compliqué. Car il faut penser que vous pouvez faire deux choses en même temps ce qui implique des problèmes:

Que ce passe t'il si on écrit dans le même fichier depuis 2 threads?

Dès que l'on utilise une architecture multi-thread on est confronté à ce genre de problèmes, que je ne détaillerai pas ici.

Afin de mieux comprendre l'utilité, voici par exemple une idée d'application dans un cas concret avec un Eggdrop:

Vous avez fait un script de stats qui log tout les salons, et tous les soirs à minuit vous lancez 'pisg', ou n'importe quel autre logiciel pour générer les stats automatiquement depuis un script Tcl.

Si vous travaillez sur un GROS fichier de log (des milliers de lignes), votre Eggdrop va complètement "freezer" durant toute l'execution de pisg, qui peut durer plusieurs minutes, et aboutit à un timeout.

En utilisant un Thread séparé, votre Eggdrop continuera à faire son travail, et vous profiterez pleinement du ou des autres processeurs libres sur le systèmes pour générer tranquillement vos statistiques: Un gain de performance important!

J'espère que cette introduction aura servit à quelques un d'entre vous, si vous avez des questions, n'hésitez pas à les poser à la suite de ce sujet.
Répondre Avertir
#2
bonjour,
je schematise un peu ce qui suit...
j'ai une tres longue boucle sur des elements. Et a chaque element, je dois tester une condition et si elle est vraie, alors je dois reporter cet element dans un logfile.
A chaque element, comme je dois faire la meme procedure, comment peut-on utiliser multi-thread pour aller plus vite?
Merci de votre expertise,
LN
Répondre
#3
Très intéressant,

Mais entre les threads il n'y a pas moyen d’échanger des informations ?

Du genre thread1 fais appelle a proc1, et le résultats retourné par thread1 l’envoie a thread2 pour qu'il fasse la suite ?

C'est plus dans l'idée de processus qui s exécute a un temps précis, et, isolé ?
Répondre Avertir
#4
De mémoire (et sans rester focalisé sur eggdrop ou le tcl), le principe du multithread est d'avoir des traitements en parallèle, quasiment indépendants. Donc, on sait si un thread est lancé ou arrêté, mais ils ne communiquent pas.
Si un thread doit attendre une info d'un autre, on sort donc de ce système. Rien n'empèche que la fin d'exécution de thread1 lance thread2 (en lui passant ou non des infos), mais là c'est du séquentiel, ou plutôt du procédural.
Répondre


Atteindre :


Utilisateur(s) parcourant ce sujet : 1 visiteur(s)
Tchat 100% gratuit -Discutez en toute liberté