IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Multithreading Discussion :

Attente de la fin d'un parmis plusieurs threads


Sujet :

Multithreading

  1. #1
    Membre régulier
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    214
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 214
    Points : 115
    Points
    115
    Par défaut Attente de la fin d'un parmis plusieurs threads
    Slt, j'ai un thread qui va en créer plusieurs et leur donner chacun une tache. Je voudrais que ce thread sur veille ensuite tous les threads et dès qu'un a terminé, il lui envoie du travail. J'ai bien sûr pensé à une boucle qui vérifie tout le temps qu'un thread soit fini si oui, on lui envoie du boulot mais c'est assez barbare ! Existe-t-il donc une fonction à peu prés équivalente à un select pour les sockets ou fifo qui permet de bloquer jusqu'à ce qu'un ait fini ? J'ai sinon pensé à faire réceptionner les signaux de fin mais après comment faire attendre le thread sans faire d'attente active ? Une sorte de wait signal quoi !

    Merci d'avance !

  2. #2
    yan
    yan est déconnecté
    Rédacteur
    Avatar de yan
    Homme Profil pro
    Ingénieur expert
    Inscrit en
    Mars 2004
    Messages
    10 033
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Ille et Vilaine (Bretagne)

    Informations professionnelles :
    Activité : Ingénieur expert
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Mars 2004
    Messages : 10 033
    Points : 13 968
    Points
    13 968
    Par défaut
    Salut.
    Que veut tu dire par une thread qui ne fait rien?

    peut être avec ceci
    http://qt.developpez.com/doc/latest/....html#finished

    ou en utilisant un pool de thread et des runnable :
    http://qt.developpez.com/doc/latest/...l.html#details
    http://qt.developpez.com/doc/latest/qrunnable.html

    Ou peut être les concurrent
    http://qt.developpez.com/doc/latest/qtconcurrent.html

  3. #3
    Membre régulier
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    214
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 214
    Points : 115
    Points
    115
    Par défaut
    Ben en fait, je voudrais "surveiller" les threads et dès qu'un a fini que j'en soit informé. Comme la fonction select. Effectivement, comme vous m'avez proposé, je peux agir dès que je reçoit un signal d'un thread qui se termine. Mais je fais quoi comme attente ? Obligatoirement une attente active ? Ou existe-t-il un moyen "d'attendre" un signal de manière passive (en évitant une sorte de boucle infinie ! ) ?

    Merci d'avance !

  4. #4
    yan
    yan est déconnecté
    Rédacteur
    Avatar de yan
    Homme Profil pro
    Ingénieur expert
    Inscrit en
    Mars 2004
    Messages
    10 033
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Ille et Vilaine (Bretagne)

    Informations professionnelles :
    Activité : Ingénieur expert
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Mars 2004
    Messages : 10 033
    Points : 13 968
    Points
    13 968
    Par défaut
    Citation Envoyé par barbug Voir le message
    Effectivement, comme vous m'avez proposé, je peux agir dès que je reçoit un signal d'un thread qui se termine. Mais je fais quoi comme attente ?
    ben rien, tu as l'eventloop qui tourne, mais ne fait rien, et dés que le signal est emit, un slot est exécuté.

    Si tes thread effectue des traitements très rapide, je pense qu'une solution avec un pool de thread et des runnable sera plus efficace. Ou avec les QtConcurrent. Histoire de ne pas recréé un thread à chaque fois.


    [edit]
    tu peut tutoyer

  5. #5
    Membre expert

    Avatar de IrmatDen
    Profil pro
    Inscrit en
    Novembre 2006
    Messages
    1 727
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2006
    Messages : 1 727
    Points : 3 266
    Points
    3 266
    Par défaut
    Salut,

    Pourquoi ne pas avoir une approche légèrement différente ?

    * Le maitre (M1) possède n travailleurs
    * chaque travailleur possède sa propre file de tâche(s)
    * dès que le travailleur a fini sa file de tâche, il se met en sommeil (comme l'a dit mongaulois, ça passerait par une boucle d'événement locale au thread)
    * Dès que le maitre reçoit une tache d'un quelconque autre maitre (M2 pour faire la distinction), il trouve le thread qui a le moins de tâches en file et lui assigne
    * Si le thread n'avait pas de tâches en file ni de tâche en cours, le maitre réveille le thread par un signal (toujours comme l'a dit mongaulois )

    Le "problème" de Qt Concurrent est qu'il ne supporte pas l'ajout de tâches contrairement à TBB. Tu donnes une série de tâche et il les exécute; c'est tout.

  6. #6
    yan
    yan est déconnecté
    Rédacteur
    Avatar de yan
    Homme Profil pro
    Ingénieur expert
    Inscrit en
    Mars 2004
    Messages
    10 033
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Ille et Vilaine (Bretagne)

    Informations professionnelles :
    Activité : Ingénieur expert
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Mars 2004
    Messages : 10 033
    Points : 13 968
    Points
    13 968
    Par défaut
    Citation Envoyé par IrmatDen Voir le message
    Le "problème" de Qt Concurrent est qu'il ne supporte pas l'ajout de tâches contrairement à TBB. Tu donnes une série de tâche et il les exécute; c'est tout.
    Quand j'avais regardé de plus prés, il me semblais que tout les appel concurrent on le même pool de thread et donc chaque appel était en attente jusqu'à ce qu'une thread est libre.
    Donc pour moi, cela reviendrais au même.

  7. #7
    Membre expert

    Avatar de IrmatDen
    Profil pro
    Inscrit en
    Novembre 2006
    Messages
    1 727
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2006
    Messages : 1 727
    Points : 3 266
    Points
    3 266
    Par défaut
    Non, ça ne revient pas au même. A chaque notification de tâche à effectuer, tu vas te retrouver à instancier un QFuture. Même si le pool de thread est commun, c'est plus que moyen de tracker tout ça. Autant avoir une approche explicite quant à la répartition des tâches (sans compter que tu peux justement en faire une meilleure si tu ne passes pas par ça, ie avoir des workers "spécialisés" dans certain type de tâches, ou alterner la répartition entre tâche longue / tâche courte pour chaque file pour éviter d'engorger des queues etc...).

    Les approches de haut niveau, c'est bien, mais dans une certaine limite et pour certaine fonctions. Après... peut-être que Qt Concurrent répond aux attentes de barbug, mais tel qu'il décrit son besoin, j'en ai pas l'impression.

  8. #8
    Membre régulier
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    214
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 214
    Points : 115
    Points
    115
    Par défaut
    Le problème pour la liste d'attente c'est que je n'ai aucune idée du temps que va mettre chaque thread selon chaque tache (ça peut aller d'un dixième de secondes à heures !) Donc je préfère attendre. De plus, je ne sais pas vraiment utiliser les pool de threads, donc, en tenant compte du temps restant et du travail qu'il me reste à faire, je vais rester dans mes connaissances minimes au maximum ! L'eventloop semble m'intéresser, ça marche comment ? J'ai regardé sur google, mais seulement le premier résultat en parle mais c'est un mail de Qt 1.33... En fait, j'avais pensé faire comme attente un truc genre

    while(1) { sleep(1); }

    Mais niveau optimisé, c'est vraiment pas top. Par contre tout ce qui est pool de thread, etc, je m'y interesserai bientôt !

    Merci d'avance !

  9. #9
    yan
    yan est déconnecté
    Rédacteur
    Avatar de yan
    Homme Profil pro
    Ingénieur expert
    Inscrit en
    Mars 2004
    Messages
    10 033
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Ille et Vilaine (Bretagne)

    Informations professionnelles :
    Activité : Ingénieur expert
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Mars 2004
    Messages : 10 033
    Points : 13 968
    Points
    13 968
    Par défaut
    Pour lancer l'eventloop dans une thread, il suffit d'executer exec() dans le run.
    Regarde les exemples dans la doc, tu trouvera ce qu'il te faut.

  10. #10
    Membre expert

    Avatar de IrmatDen
    Profil pro
    Inscrit en
    Novembre 2006
    Messages
    1 727
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2006
    Messages : 1 727
    Points : 3 266
    Points
    3 266
    Par défaut
    J'ai fait un exemple d'un thread maitre (QThread) qui distribue des tâches au QThreadPool.
    J'ai commenté dans les grandes lignes ce qu'il se passe, ça devrait te permettre de voir comment donner du travail au thread pool tout en restant à l'écoute de l'état des tâches lancées (ici, je surveille lorsqu'elles se terminent, c'est tout, mais tu peux utiliser tout ce que tu veux comme signaux/slots, comme d'hab).

    C'est une appli console qui va lancer 10 tâches; le thread pool a été forcé à 4 threads simultanés (pas forcément recommandé en vrai, mais l'exemple montre assurément plusieurs thread de cette façon).

    Si tu saisis un int supérieur à 0, une tâche sera ajoutée, si la saisie est inférieur ou égal à 0, on quitte.
    Si tu saisis autre chose qu'un chiffre, ça péte parce que le but n'est pas de montrer une bonne gestion de la saisie utilisateur
    Il n'y a aucun lien entre le chiffre tapé, et la durée de la tâche, c'est juste son signe qui compte.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    // Fichier: main.cpp
    #include <iostream>
    #include <sstream>
    #include <string>
     
    #include <QtCore>
    #include <QtDebug>
     
    using namespace std;
     
    // On fournit une façon thread-safe d'écrire sur la console.
    // (l'écho fournit par cin n'est *pas* concerné par l'aspect threadsafe!)
    class LoggingApplication : public QCoreApplication
    {
    public:
    	LoggingApplication(int argc, char **argv)
    	:QCoreApplication(argc, argv)
    	{
    	}
     
    	void log(std::string str)
    	{
    		QMutexLocker locker(&outMutex);
    		std::cout << str;
    	}
     
    private:
    	QMutex outMutex;
    };
     
    // Cette classe représente une tache; la tache est représentée par une durée aléatoire.
    // Le résultat obtensible est la durée convertit en QString (1->a, 2->b, etc...).
    class Task : public QObject, public QRunnable
    {
    Q_OBJECT
     
    public:
    	Task(int taskIdx, int timeToFillTask)
    	:taskIndex(taskIdx), taskDuration(timeToFillTask)
    	{
    	}
     
    	// Cette fonction sera exécutée par l'un des thread de la thread pool globale.
    	virtual void run()
    	{
    		QTime startTime(0, 0);
    		startTime.start();
     
    		ostringstream oss;
    		oss << "Runnable: Starting task #" << taskIndex << " (duration: "
    			<< taskDuration << " ms)\n";
    		LoggingApplication *app = (LoggingApplication*)QCoreApplication::instance();
    		app->log(oss.str());
     
    		while (startTime.elapsed() < taskDuration)
    		{
    			// on ne fait rien, on simule juste l'exécution d'une tâche de longueur variable
    		}
     
    		if (taskDuration > 1000)
    			result  = (char)('a' +  (taskDuration / 1000));
    		if (taskDuration > 100)
    			result += (char)('a' + ((taskDuration / 100) % 10));
    		if (taskDuration > 10)
    			result += (char)('a' + ((taskDuration / 10) % 10));
    		if (taskDuration > 0)
    			result += (char)('a' + (taskDuration % 10));
     
    		emit finished(taskIndex);
    	}
     
    	QString getResult() const
    	{
    		return result;
    	}
     
    signals:
    	void finished(int taskIdx);
     
    private:
    	int taskIndex;
    	int taskDuration;
    	QString result;
    };
     
    class MasterThread : public QThread
    {
    Q_OBJECT
     
    public:
    	const int maxTaskLength;
    	const int minTaskLength;
     
    public:
    	MasterThread()
    	:maxTaskLength(3000), minTaskLength(500), initialTaskCount(10), taskFinishedCount(0)
    	{
    		for (int i = 0; i < initialTaskCount; i++)
    		{
    			int duration = qrand() / (RAND_MAX / (maxTaskLength - minTaskLength) + 1);
    			duration += minTaskLength;
    			addTask(duration, false);
    		}
    	}
     
    	void addTask(int taskDuration, bool startASAP)
    	{
    		Task *t = new Task(tasks.size(), taskDuration);
     
    		// on veut récupérer le résultat à la fin de l'exécution de la tâche,
    		// donc le thread pool ne doit pas le supprimer
    		t->setAutoDelete(false);
     
    		tasks.push_back(t);
    		bool b = connect(t, SIGNAL(finished(int)), this, SLOT(onTaskFinished(int)), Qt::QueuedConnection);
    		if (!b)
    		{
    			LoggingApplication *app = (LoggingApplication*)QCoreApplication::instance();
    			ostringstream oss;
    			oss << "Failed connection of task #" << tasks.size() << "\n";
    			app->log(oss.str());
    		}
     
    		if(startASAP)
    		{
    			QThreadPool *threadPool = QThreadPool::globalInstance();
    			threadPool->start(t);
    		}
    	}
     
    protected:
    	virtual void run()
    	{
    		// On commence toutes les tâches préprogrammées
    		QThreadPool *threadPool = QThreadPool::globalInstance();
    		threadPool->setMaxThreadCount(4); // On demande 4 tâches à la fois
    		foreach(Task *t, tasks)
    			threadPool->start(t);
     
    		// L'event loop tourne pour laisser les signaux des tâches atteindre ce thread
    		// tant que quit() n'a pas été appelé
    		LoggingApplication *app = (LoggingApplication*)QCoreApplication::instance();
    		app->log("Master thread: enter event loop\n");
    		exec();
    		app->log("Master thread leave event loop\n");
     
    		// Une demande d'arrêt a été effectuée, on attend le thread pool.
    		// (Toutes les tâches en queues sont exécutées avant le retour de cette fonction.)
    		threadPool->waitForDone();
    		app->log("Master thread: threadpool finished its queue\n");
     
    		// Ici, un nombre négatif peut être affiché si on a quitté le programme
    		// avant la fin de l'exécution de chacune des tâches.
    		ostringstream oss;
    		oss << "Master thread: Finished executing " << taskFinishedCount << " tasks ("
    			<< taskFinishedCount - initialTaskCount << " supplementary tasks performed)\n";
    		app->log(oss.str());
    	}
     
    public slots:
    	void onTaskFinished(int taskIdx)
    	{
    		ostringstream oss;
    		oss << "Master thread: Task #" << taskIdx << " finished. Result: "
    			<< tasks[taskIdx]->getResult().toStdString() << "\n";
    		LoggingApplication *app = (LoggingApplication*)QCoreApplication::instance();
    		app->log(oss.str());
     
    		delete tasks[taskIdx];
    		tasks[taskIdx] = 0;
    		taskFinishedCount++;
    	}
     
    private:
    	int initialTaskCount;
    	QVector<Task*> tasks;
    	int taskFinishedCount;
    };
     
    // Ce thread va juste rester à l'écoute de stdin, jusqu'à saisie
    // d'un nombre négatif qui l'arrête (et entraîne l'arrêt de l'application).
    class InputThread : public QThread
    {
    public:
    	InputThread(MasterThread &master)
    	:m(master)
    	{
    	}
     
    protected:
    	virtual void run()
    	{
    		int i;
    		while (cin >> i)
    		{
    			if(i > 0)
    			{
    				int duration = qrand() / (RAND_MAX / m.maxTaskLength - m.minTaskLength + 1);
    				duration += m.minTaskLength;
    				m.addTask(duration, true);
    			}
    			else
    			{
    				cout << "Input thread: Request exit\n";
    				m.quit(); // On demande un arrêt du thread
    				cout << "Input thread: Wait for master thread\n";
    				m.wait();
    				break;
    			}
    		}
    	}
     
    private:
    	MasterThread &m;
    };
     
    #include "main.moc"
     
    int main(int argc, char **argv)
    {
    	LoggingApplication app(argc, argv);
     
    	qsrand(42);
     
    	// NB: ici on passe par les entrées/sorties standard qui ne sont pas thread-safe.
    	// conséquence possible: on peut avoir la sortie d'une tâche qui apparaît pendant qu'on tape un chiffre.
    	cout << "Un chiffre superieur à 0 cree une nouvelle tache; tout autre chiffre quitte:" << endl;
     
    	MasterThread master;
    	master.start();
     
    	InputThread input(master);
    	input.start();
     
    	app.connect(&input, SIGNAL(finished()), &app, SLOT(quit()));
     
    	app.exec();
    }
    .pro:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    ######################################################################
    # Automatically generated by qmake (2.01a) jeu. 12. févr. 01:52:50 2009
    ######################################################################
     
    TEMPLATE = app
    CONFIG += console
    TARGET = 
    DEPENDPATH += .
    INCLUDEPATH += .
     
    # Input
    SOURCES += main.cpp
    Exemple de sortie obtenue avec 4 tâches supplémentaires:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    Un chiffre superieur Ó 0 cree une nouvelle tache; tout autre chiffre quitte:
    Runnable: Starting task #0 (duration: 512 ms)
    Runnable: Starting task #1 (duration: 528 ms)
    Runnable: Starting task #2 (duration: 1776 ms)
    Runnable: Starting task #3 (duration: 2646 ms)
    Master thread: enter event loop
    Runnable: Starting task #4 (duration: 1648 ms)
    Master thread: Task #0 finished. Result: fbc
    Runnable: Starting task #5 (duration: 1419 ms)
    Master thread: Task #1 finished. Result: fci
    Master thread: Task #2 finished. Result: bhhg
    Runnable: Starting task #6 (duration: 1072 ms)
    Runnable: Starting task #7 (duration: 1046 ms)
    Master thread: Task #5 finished. Result: bebj
    Runnable: Starting task #8 (duration: 1629 ms)
    Runnable: Starting task #9 (duration: 626 ms)
    Master thread: Task #4 finished. Result: bgei
    Master thread: Task #3 finished. Result: cgeg
    Master thread: Task #6 finished. Result: bahc
    Master thread: Task #7 finished. Result: baeg
    Master thread: Task #9 finished. Result: gcg
    Master thread: Task #8 finished. Result: bgcj
    42
    Runnable: Starting task #10 (duration: 500 ms)
    Master thread: Task #10 finished. Result: faa
    53
    Runnable: Starting task #11 (duration: 463 ms)
    Master thread: Task #11 finished. Result: egd
    112
    Runnable: Starting task #12 (duration: 488 ms)
    Master thread: Task #12 finished. Result: eii
    1
    Runnable: Starting task #13 (duration: 446 ms)
    Master thread: Task #13 finished. Result: eeg
    -1
    Input thread: Request exit
    Input thread: Wait for master thread
    Master thread leave event loop
    Master thread: threadpool finished its queue
    Master thread: Finished executing 14 tasks (4 supplementary tasks performed)

  11. #11
    Membre régulier
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    214
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 214
    Points : 115
    Points
    115
    Par défaut
    Trés intéressant ! Merci beaucoup ! Seul petit truc que je n'ai pas compris c'est le exec(). A quoi sert-il ? A ce que j'ai compris, il rentrer dans l'eventloop. C'est à dire ? Comment sort-il ? Dès qu'il reçoit un signal spécifique ? Ou dès qu'on appelle sa fonction quit ? (de ce que j'ai compris du thread d'entrée !)

    Merci d'avance ! je penses que ça va régler mon problème !

  12. #12
    Membre expert

    Avatar de IrmatDen
    Profil pro
    Inscrit en
    Novembre 2006
    Messages
    1 727
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2006
    Messages : 1 727
    Points : 3 266
    Points
    3 266
    Par défaut
    En effet, exec sert à lancer une event loop dans un thread (l'appel dans main en lance une dans le thread principal, celui de MasterThread en lance une spécifique à son thread). Comme l'a indiqué mongaulois plus haut, ça permet aux signaux/slots de fonctionner entre thread puisqu'ils sont alors transmis comme des événements et non plus comme des appels de fonctions directs.

    Pour l'arrêter, c'est en effet en appelant la fonction quit() correspondante, qui est un slot de QEventLoop. Les classes QCoreApplication et QThread fournissent un raccourci vers ce slot.

  13. #13
    Membre régulier
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    214
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 214
    Points : 115
    Points
    115
    Par défaut
    Parfait ! Merci beaucoup, c'est exactement ce que je recherchais !

    A bientôt !

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Attente d'un thread parmis plusieurs
    Par sone47 dans le forum Threads & Processus
    Réponses: 2
    Dernier message: 06/03/2013, 10h47
  2. [VBA-E]Fermer classeur EXCEL parmi plusieurs
    Par flyfrog dans le forum Macros et VBA Excel
    Réponses: 9
    Dernier message: 30/04/2006, 11h54
  3. Sélection d'enregistrement parmis plusieurs identiques
    Par monnoliv dans le forum Décisions SGBD
    Réponses: 2
    Dernier message: 24/09/2005, 15h32
  4. Réponses: 2
    Dernier message: 24/08/2005, 10h59
  5. sélection d'un max parmi plusieurs champs
    Par invitésuprise dans le forum Langage SQL
    Réponses: 2
    Dernier message: 12/08/2005, 13h49

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo