Einfaches Beispiel für das Threading in C ++

391

Kann jemand ein einfaches Beispiel für das Starten von zwei (objektorientierten) Threads in C ++ veröffentlichen?

Ich suche nach tatsächlichen C ++ - Thread-Objekten, auf die ich Ausführungsmethoden (oder ähnliches) erweitern kann, anstatt eine Thread-Bibliothek im C-Stil aufzurufen.

Ich habe alle betriebssystemspezifischen Anfragen ausgelassen, in der Hoffnung, dass jeder, der geantwortet hat, mit plattformübergreifenden Bibliotheken antworten würde. Ich mache das jetzt nur explizit.

Zak
quelle

Antworten:

561

Erstellen Sie eine Funktion, die der Thread ausführen soll, z.

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

Erstellen Sie nun das threadObjekt, das letztendlich die obige Funktion aufruft, wie folgt:

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

(Sie müssen auf #include <thread>die std::threadKlasse zugreifen )

Die Argumente des Konstruktors sind die Funktion, die der Thread ausführen wird, gefolgt von den Parametern der Funktion. Der Thread wird beim Aufbau automatisch gestartet.

Wenn Sie später warten möchten, bis der Thread die Funktion ausgeführt hat, rufen Sie Folgendes auf:

t1.join(); 

(Beitritt bedeutet, dass der Thread, der den neuen Thread aufgerufen hat, darauf wartet, dass der neue Thread die Ausführung beendet, bevor er seine eigene Ausführung fortsetzt.)


Der 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();
}

Weitere Informationen zu std :: thread finden Sie hier

  • Kompilieren Sie auf GCC mit -std=c++0x -pthread.
  • Dies sollte für jedes Betriebssystem funktionieren, vorausgesetzt, Ihr Compiler unterstützt diese Funktion (C ++ 11).
MasterMastic
quelle
4
@ Preza8 VS10 unterstützt nicht viele Funktionen in C ++ 11. VS12- und VS13-Support-Thread.
Jodag
3
Verwenden Sie im Kommentar "GCC-Versionen unter 4.7 -std=c++0x(anstelle von -std=c++0x)". Ich glaube, das zweite "c ++ 0x" sollte stattdessen "c ++ 11" sein, aber das kann nicht geändert werden, da die Bearbeitung zu klein ist.
zentrunix
1
@MasterMastic Welchen Unterschied macht es, wenn wir anstelle von std :: thread t1 (task1, "Hallo") std :: thread t1 (& task1, "Hallo") verwenden und die Referenz der Funktion übergeben? Sollten wir in diesem Fall die Funktion als Zeiger deklarieren?
user2452253
3
@ user2452253 Wenn Sie "task1" übergeben, übergeben Sie einen Zeiger auf die Funktion, die Sie ausführen möchten (was gut ist). Wenn Sie "& task1" übergeben, übergeben Sie einen Zeiger auf einen Zeiger einer Funktion, und die Ergebnisse wären wahrscheinlich nicht so hübsch und sehr undefiniert (es wäre, als würden Sie versuchen, zufällige Anweisungen nacheinander auszuführen. Mit der richtigen Wahrscheinlichkeit könnten Sie dies auch tun öffne das Tor zur Hölle). Sie möchten wirklich nur den Funktionszeiger übergeben (einen Zeiger mit dem Wert der Adresse, an der die Funktion beginnt). Wenn das nicht klärt, lass es mich wissen und viel Glück!
MasterMastic
2
@curiousguy Nun, der richtige Weg, dies zu tun, besteht darin, einen Zeiger auf die Funktion zu übergeben, die Sie ausführen möchten. In diesem Fall ist die Funktion task1. Der Funktionszeiger wird also auch mit bezeichnet task1. Aber danke, dass Sie dies angesprochen haben, denn ich glaube, ich habe mich geirrt und es ist gleichbedeutend &task1damit. Es spielt also keine Rolle, welche Form Sie schreiben (wenn ja, denke ich, dass Zeiger auf Zeiger der Funktion ist &&task1- das wäre schlecht ). Relevante Frage .
MasterMastic
80

Technisch gesehen wird ein solches Objekt über eine Thread-Bibliothek im C-Stil erstellt, da C ++ std::threadin c ++ 0x nur ein Aktienmodell angegeben hat, das gerade festgenagelt und noch nicht implementiert wurde. Das Problem ist etwas systematisch. Technisch gesehen ist das vorhandene C ++ - Speichermodell nicht streng genug, um eine genau definierte Semantik für alle Fälle zu ermöglichen, die vorher auftreten. Hans Boehm hat vor einiger Zeit einen Artikel zu diesem Thema geschrieben und war maßgeblich daran beteiligt, den c ++ 0x-Standard zu diesem Thema auszuarbeiten.

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

Es gibt jedoch mehrere plattformübergreifende Thread-C ++ - Bibliotheken, die in der Praxis einwandfrei funktionieren. Intel-Thread-Bausteine ​​enthalten ein tbb :: thread-Objekt, das dem c ++ 0x-Standard sehr nahe kommt, und Boost verfügt über eine boost :: thread-Bibliothek, die dasselbe tut.

http://www.threadingbuildingblocks.org/

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

Mit boost :: thread erhalten Sie Folgendes:

#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;
}
Edward KMETT
quelle
8
Boost-Thread ist großartig - mein einziges Problem war, dass Sie (als ich es das letzte Mal verwendet habe) nicht auf das native zugrunde liegende Thread-Handle zugreifen konnten, da es ein privates Klassenmitglied war! In win32 gibt es eine Menge Dinge, für die Sie den Threadhandle benötigen. Deshalb haben wir ihn optimiert, um den Handle öffentlich zu machen.
Orion Edwards
4
Ein weiteres Problem mit boost :: thread ist, dass Sie, wie ich mich erinnere, nicht die Freiheit haben, die Stapelgröße des neuen Threads festzulegen - eine Funktion, die leider auch nicht im c ++ 0x-Standard enthalten ist.
Edward KMETT
Dieser Code gibt mir eine Boost :: nicht kopierbare Ausnahme für diese Zeile: thread thread_1 = thread (task1); Vielleicht liegt es daran, dass ich eine ältere Version (1.32) verwende.
Frank
task1 wurde in diesem Bereich nicht deklariert?
Jake Gaston
20

Es gibt auch eine POSIX-Bibliothek für POSIX-Betriebssysteme. Überprüfen Sie die Kompatibilität

#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;

}

Kompilieren Sie mit -lpthread

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

Hohenheimsenberg
quelle
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);
}
Caner
quelle
13

Bei der Suche nach einem Beispiel für eine C ++ - Klasse, die eine ihrer eigenen Instanzmethoden in einem neuen Thread aufruft, wird diese Frage gestellt, aber wir konnten keine dieser Antworten auf diese Weise verwenden. Hier ist ein Beispiel dafür:

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
    }
}

Beachten Sie, dass dieses Beispiel nicht in Mutex oder Sperren gerät.

livingtech
quelle
2
Vielen Dank für die Veröffentlichung. Beachten Sie, dass die allgemeine Form des Aufrufs einer Thread-on-Instance-Methode ungefähr so ​​lautet: Foo f; std :: thread t (& Foo :: Run, & f, args ...); (wobei Foo eine Klasse ist, die 'Run ()' als Mitgliedsfunktion hat).
Jay Elston
Vielen Dank für die Veröffentlichung. Ich verstehe ernsthaft nicht, warum alle Threading-Beispiele globale Funktionen verwenden.
hkBattousai
9

Sofern keine separate Funktion in globalen Namespacs gewünscht wird, können Lambda-Funktionen zum Erstellen von Threads verwendet werden.

Einer der Hauptvorteile beim Erstellen von Threads mit Lambda besteht darin, dass wir keine lokalen Parameter als Argumentliste übergeben müssen. Wir können die Erfassungsliste für dasselbe verwenden und die Schließungseigenschaft von Lambda kümmert sich um den Lebenszyklus.

Hier ist ein Beispielcode

int main() {
    int localVariable = 100;

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

    th.join();

    return 0;
}

Bei weitem habe ich festgestellt, dass C ++ - Lambdas die beste Methode zum Erstellen von Threads sind, insbesondere für einfachere Threadfunktionen

Daksh
quelle
8

Dies hängt weitgehend von der Bibliothek ab, für die Sie sich entscheiden. Wenn Sie beispielsweise die Bibliothek wxWidgets verwenden, sieht die Erstellung eines Threads folgendermaßen aus:

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;
}

Wenn Ihr Hauptthread die CreateThread-Methode aufruft, erstellen Sie einen neuen Thread, der den Code in Ihrer "Entry" -Methode ausführt. In den meisten Fällen müssen Sie einen Verweis auf den Thread behalten, um ihn zu verbinden oder zu stoppen. Weitere Infos hier: wxThread-Dokumentation

LorenzCK
quelle
1
Sie haben vergessen, den Thread zu löschen. Vielleicht wollten Sie einen getrennten Thread erstellen ?
Aib
1
Ja, richtig, ich habe den Code aus einem Code extrahiert, an dem ich gerade arbeite, und natürlich wird der Verweis auf den Thread irgendwo gespeichert, um ihn später zu verbinden, anzuhalten und zu löschen. Vielen Dank. :)
LorenzCK