Exemple simple de threading en C ++


391

Quelqu'un peut-il publier un exemple simple de démarrage de deux threads (orientés objet) en C ++.

Je recherche des objets de thread C ++ réels sur lesquels je peux étendre les méthodes d'exécution (ou quelque chose de similaire) au lieu d'appeler une bibliothèque de threads de style C.

J'ai omis toutes les demandes spécifiques au système d'exploitation dans l'espoir que celui qui répondrait répondrait avec les bibliothèques multiplateformes à utiliser. Je ne fais que rendre cela explicite maintenant.


Réponses:


562

Créez une fonction que vous souhaitez que le thread exécute, par exemple:

void task1(std::string msg)
{
    std::cout << "task1 says: " << msg;
}

Créez maintenant l' threadobjet qui appellera finalement la fonction ci-dessus comme ceci:

std::thread t1(task1, "Hello");

(Vous devez #include <thread>accéder à la std::threadclasse)

Les arguments du constructeur sont la fonction que le thread exécutera, suivie des paramètres de la fonction. Le fil démarre automatiquement lors de la construction.

Si plus tard vous voulez attendre que le thread ait fini d'exécuter la fonction, appelez:

t1.join(); 

(La jointure signifie que le thread qui a appelé le nouveau thread attendra la fin de l'exécution du nouveau thread, avant de poursuivre sa propre exécution).


Le code

#include <string>
#include <iostream>
#include <thread>

using namespace std;

// The function we want to execute on the new thread.
void task1(string msg)
{
    cout << "task1 says: " << msg;
}

int main()
{
    // Constructs the new thread and runs it. Does not block execution.
    thread t1(task1, "Hello");

    // Do other things...

    // Makes the main thread wait for the new thread to finish execution, therefore blocks its own execution.
    t1.join();
}

Plus d'informations sur std :: thread ici

  • Sur GCC, compilez avec -std=c++0x -pthread.
  • Cela devrait fonctionner pour n'importe quel système d'exploitation, étant donné que votre compilateur prend en charge cette fonctionnalité (C ++ 11).

4
@ Preza8 VS10 ne prend pas en charge de nombreuses fonctionnalités en C ++ 11. Fil de support VS12 et VS13.
jodag

3
Dans le commentaire "Versions GCC inférieures à 4.7, utilisez -std=c++0x(au lieu de -std=c++0x)" Je pense que le deuxième "c ++ 0x" devrait plutôt être "c ++ 11", mais ce n'est pas possible de le changer car la modification est trop petite.
zentrunix

1
@MasterMastic Quelle différence cela fait-il si au lieu de std :: thread t1 (tâche1, "Bonjour") nous utilisons std :: thread t1 (& tâche1, "Bonjour"), en passant la référence de la fonction? Faut-il déclarer la fonction comme pointeur dans ce cas?
user2452253

3
@ user2452253 Si vous passez "task1", ​​vous passez un pointeur sur la fonction que vous souhaitez exécuter (ce qui est bien). Si vous passez "& task1", ​​vous passez un pointeur vers le pointeur d'une fonction, et les résultats ne seraient probablement pas si jolis et très indéfinis (ce serait comme essayer d'exécuter des instructions aléatoires les unes après les autres. Avec la bonne probabilité, vous pourriez juste ouvrir la porte de l'enfer). Vous voulez vraiment juste passer le pointeur de fonction (un pointeur avec la valeur de l'adresse où la fonction commence). Si cela ne clarifie pas les choses, faites-le moi savoir et bonne chance!
MasterMastic

2
@curiousguy Eh bien, la bonne façon de le faire est de passer un pointeur de la fonction que vous souhaitez exécuter. Dans ce cas, la fonction est task1. Ainsi, le pointeur de fonction est également désigné par task1. Mais merci d'avoir soulevé cette question parce que je crois que j'avais tort et que c'est équivalent à &task1, donc peu importe la forme que vous choisissez d'écrire (si c'est le cas, je suppose que pointeur à pointeur de la fonction est &&task1- ce serait mauvais ). Question pertinente .
MasterMastic

80

Eh bien, techniquement, un tel objet finira par être construit sur une bibliothèque de threads de style C car C ++ vient de spécifier un std::threadmodèle de stock en c ++ 0x, qui vient d'être cloué et n'a pas encore été implémenté. Le problème est quelque peu systémique, techniquement, le modèle de mémoire c ++ existant n'est pas assez strict pour permettre une sémantique bien définie pour tous les cas «avant». Hans Boehm a écrit un article sur le sujet il y a quelque temps et a joué un rôle déterminant dans l'élaboration de la norme c ++ 0x sur le sujet.

http://www.hpl.hp.com/techreports/2004/HPL-2004-209.html

Cela dit, il existe plusieurs bibliothèques C ++ de threads multiplateforme qui fonctionnent très bien dans la pratique. Les blocs de construction de threads Intel contiennent un objet tbb :: thread qui se rapproche étroitement de la norme c ++ 0x et Boost a une bibliothèque boost :: thread qui fait de même.

http://www.threadingbuildingblocks.org/

http://www.boost.org/doc/libs/1_37_0/doc/html/thread.html

En utilisant boost :: thread, vous obtiendrez quelque chose comme:

#include <boost/thread.hpp>

void task1() { 
    // do stuff
}

void task2() { 
    // do stuff
}

int main (int argc, char ** argv) {
    using namespace boost; 
    thread thread_1 = thread(task1);
    thread thread_2 = thread(task2);

    // do other stuff
    thread_2.join();
    thread_1.join();
    return 0;
}

8
Boost thread est génial - mon seul problème était que vous ne pouviez pas (quand je l'ai utilisé pour la dernière fois) accéder réellement au handle de thread sous-jacent natif car il était un membre de classe privé! Il y a une tonne de choses dans win32 pour lesquelles vous avez besoin de la poignée de fil, nous l'avons donc peaufinée pour rendre la poignée publique.
Orion Edwards

4
Un autre problème avec boost :: thread est que, si je me souviens bien, vous n'avez pas la liberté de définir la taille de pile du nouveau thread - une fonctionnalité qui n'est malheureusement pas non plus incluse dans la norme c ++ 0x.
Edward KMETT

Ce code me donne une exception boost :: noncopyable pour cette ligne: thread thread_1 = thread (task1); C'est peut-être parce que j'utilise une ancienne version (1.32).
Frank

task1 n'a pas été déclarée dans cette étendue?
Jake Gaston

20

Il existe également une bibliothèque POSIX pour les systèmes d'exploitation POSIX. Vérifier la compatibilité

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <iostream>

void *task(void *argument){
      char* msg;
      msg = (char*)argument;
      std::cout<<msg<<std::endl;
}

int main(){
    pthread_t thread1, thread2;
    int i1,i2;
    i1 = pthread_create( &thread1, NULL, task, (void*) "thread 1");
    i2 = pthread_create( &thread2, NULL, task, (void*) "thread 2");

    pthread_join(thread1,NULL);
    pthread_join(thread2,NULL);
    return 0;

}

compiler avec -lpthread

http://en.wikipedia.org/wiki/POSIX_Threads


18
#include <thread>
#include <iostream>
#include <vector>
using namespace std;

void doSomething(int id) {
    cout << id << "\n";
}

/**
 * Spawns n threads
 */
void spawnThreads(int n)
{
    std::vector<thread> threads(n);
    // spawn n threads:
    for (int i = 0; i < n; i++) {
        threads[i] = thread(doSomething, i + 1);
    }

    for (auto& th : threads) {
        th.join();
    }
}

int main()
{
    spawnThreads(10);
}

13

Lorsque vous recherchez un exemple d'une classe C ++ qui appelle l'une de ses propres méthodes d'instance dans un nouveau thread, cette question se pose, mais nous n'avons pas pu utiliser l'une de ces réponses de cette façon. Voici un exemple qui fait cela:

Class.h

class DataManager
{
public:
    bool hasData;
    void getData();
    bool dataAvailable();
};

Class.cpp

#include "DataManager.h"

void DataManager::getData()
{
    // perform background data munging
    hasData = true;
    // be sure to notify on the main thread
}

bool DataManager::dataAvailable()
{
    if (hasData)
    {
        return true;
    }
    else
    {
        std::thread t(&DataManager::getData, this);
        t.detach(); // as opposed to .join, which runs on the current thread
    }
}

Notez que cet exemple n'entre pas dans le mutex ou le verrouillage.


2
Merci d'avoir posté ça. Notez que la forme générale d'invocation d'un thread sur une méthode d'instance ressemble à ceci: Foo f; std :: thread t (& Foo :: Run, & f, args ...); (où Foo est une classe qui a 'Run ()' comme fonction membre).
Jay Elston

Merci d'avoir posté ça. Je ne comprends vraiment pas pourquoi tous les exemples de thread utilisent des fonctions globales.
hkBattousai

9

À moins que l'on veuille une fonction distincte dans les espaces de noms globaux, nous pouvons utiliser des fonctions lambda pour créer des threads.

L'un des principaux avantages de la création de thread à l'aide de lambda est que nous n'avons pas besoin de transmettre les paramètres locaux sous forme de liste d'arguments. Nous pouvons utiliser la liste de capture pour la même chose et la propriété de fermeture de lambda se chargera du cycle de vie.

Voici un exemple de code

int main() {
    int localVariable = 100;

    thread th { [=](){
        cout<<"The Value of local variable => "<<localVariable<<endl;
    }}

    th.join();

    return 0;
}

De loin, j'ai trouvé que les lambdas C ++ étaient le meilleur moyen de créer des threads, en particulier pour les fonctions de thread plus simples


8

Cela dépend en grande partie de la bibliothèque que vous décidez d'utiliser. Par exemple, si vous utilisez la bibliothèque wxWidgets, la création d'un thread ressemblerait à ceci:

class RThread : public wxThread {

public:
    RThread()
        : wxThread(wxTHREAD_JOINABLE){
    }
private:
    RThread(const RThread &copy);

public:
    void *Entry(void){
        //Do...

        return 0;
    }

};

wxThread *CreateThread() {
    //Create thread
    wxThread *_hThread = new RThread();

    //Start thread
    _hThread->Create();
    _hThread->Run();

    return _hThread;
}

Si votre thread principal appelle la méthode CreateThread, vous allez créer un nouveau thread qui commencera à exécuter le code dans votre méthode "Entry". Vous devrez conserver une référence au thread dans la plupart des cas pour le rejoindre ou l'arrêter. Plus d'informations ici: documentation wxThread


1
Vous avez oublié de supprimer le fil. Peut-être vouliez-vous créer un fil détaché ?
BAI

1
Oui, j'ai extrait le code d'un code sur lequel je travaille actuellement et bien sûr, la référence au thread est stockée quelque part afin de le rejoindre, l'arrêter et le supprimer plus tard. Merci. :)
LorenzCK
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.