Semplice esempio di threading in C ++


391

Qualcuno può pubblicare un semplice esempio di avvio di due thread (orientati agli oggetti) in C ++.

Sto cercando oggetti thread C ++ reali su cui posso estendere i metodi di esecuzione (o qualcosa di simile) invece di chiamare una libreria thread in stile C.

Ho lasciato fuori qualsiasi richiesta specifica del sistema operativo nella speranza che chiunque rispondesse avrebbe risposto con librerie multipiattaforma da usare. Lo sto solo rendendo esplicito ora.


Risposte:


561

Crea una funzione che desideri venga eseguita dal thread, ad esempio:

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

Ora crea l' threadoggetto che alla fine invocherà la funzione sopra in questo modo:

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

(Devi #include <thread>accedere alla std::threadclasse)

Gli argomenti del costruttore sono la funzione che verrà eseguita dal thread, seguita dai parametri della funzione. Il thread viene avviato automaticamente al momento della costruzione.

Se in seguito vuoi attendere che il thread sia terminato eseguendo la funzione, chiama:

t1.join(); 

(Unire significa che il thread che ha invocato il nuovo thread attenderà che il nuovo thread termini l'esecuzione, prima che continui la propria esecuzione).


Il codice

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

Maggiori informazioni su std :: thread qui

  • Su GCC, compilare con -std=c++0x -pthread.
  • Questo dovrebbe funzionare per qualsiasi sistema operativo, a condizione che il compilatore supporti questa funzione (C ++ 11).

4
@ Preza8 VS10 non supporta molte funzionalità in C ++ 11. Discussione di supporto VS12 e VS13.
jodag

3
Nel commento "Versioni GCC precedenti alla 4.7, usa -std=c++0x(invece di -std=c++0x)" Credo che la seconda "c ++ 0x" dovrebbe essere invece "c ++ 11", ma non è possibile cambiarla perché la modifica è troppo piccola.
zentrunix,

1
@MasterMastic Che differenza fa se invece di std :: thread t1 (task1, "Hello") usiamo std :: thread t1 (& task1, "Hello"), passando il riferimento della funzione? In questo caso dovremmo dichiarare la funzione come puntatore?
user2452253

3
@ user2452253 Se si passa "task1" si passa un puntatore alla funzione che si desidera eseguire (il che è buono). Se passi "& task1" passi un puntatore a un puntatore di una funzione, e i risultati probabilmente non sarebbero così carini e molto indefiniti (sarebbe come provare a eseguire istruzioni casuali una dopo l'altra. Con la giusta probabilità potresti semplicemente apri la porta all'inferno). Vuoi davvero solo passare il puntatore alla funzione (un puntatore con il valore dell'indirizzo da cui inizia la funzione). Se ciò non chiarisce le cose, fammi sapere e buona fortuna!
MasterMastic,

2
@curiousguy Bene, il modo giusto per farlo è passare un puntatore alla funzione che si desidera eseguire. In questo caso la funzione è task1. Quindi anche il puntatore a funzione è indicato da task1. Ma grazie per aver portato questo in su perché credo mi sono sbagliato ed è equivalente a &task1, in modo che non importa quale forma si sceglie di scrivere (in tal caso, immagino che puntatore al puntatore della funzione è &&task1- che sarebbe male ). Domanda pertinente .
MasterMastic,

80

Bene, tecnicamente un tale oggetto finirà per essere costruito su una libreria di thread in stile C perché C ++ ha appena specificato un std::threadmodello di stock in c ++ 0x, che è stato appena inchiodato e non è ancora stato implementato. Il problema è in qualche modo sistemico, tecnicamente il modello di memoria c ++ esistente non è abbastanza rigido da consentire una semantica ben definita per tutti i casi "prima." Hans Boehm ha scritto un articolo sull'argomento qualche tempo fa ed è stato determinante nel mettere a punto lo standard c ++ 0x sull'argomento.

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

Detto questo, ci sono diverse librerie C ++ thread multipiattaforma che funzionano bene nella pratica. I blocchi di thread Intel contengono un oggetto tbb :: thread che si avvicina molto allo standard c ++ 0x e Boost ha una libreria boost :: thread che fa lo stesso.

http://www.threadingbuildingblocks.org/

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

Usando boost :: thread otterrai qualcosa del tipo:

#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
Il thread boost è fantastico - il mio unico problema era che non potevi (quando l'ho usato l'ultima volta) accedere effettivamente all'handle del thread sottostante nativo poiché era un membro di classe privato! C'è un sacco di roba in win32 per cui hai bisogno del threadhandle, quindi lo abbiamo modificato per rendere pubblico il handle.
Orion Edwards,

4
Un altro problema con boost :: thread è che, come ricordo, non hai la libertà di impostare le dimensioni dello stack del nuovo thread, una caratteristica che purtroppo non è inclusa nello standard c ++ 0x.
Edward KMETT,

Quel codice mi dà un'eccezione :: eccezione non copiabile per questa riga: thread thread_1 = thread (task1); Forse è perché sto usando una versione precedente (1.32).
Frank,

task1 non è stato dichiarato in questo ambito?
Jake Gaston,

20

Esiste anche una libreria POSIX per i sistemi operativi POSIX. Verifica 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;

}

compilare con -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

Quando si cerca un esempio di una classe C ++ che chiama uno dei propri metodi di istanza in un nuovo thread, viene posta questa domanda, ma non è stato possibile utilizzare nessuna di queste risposte in quel modo. Ecco un esempio che lo fa:

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

Si noti che questo esempio non entra in mutex o blocco.


2
Grazie per aver pubblicato questo Si noti che la forma generale di invocare un thread sul metodo di istanza è simile a questa: Foo f; std :: thread t (& Foo :: Run, & f, args ...); (dove Foo è una classe che ha 'Run ()' come funzione membro).
Jay Elston,

Grazie per aver pubblicato questo Sinceramente non capisco perché tutti gli esempi di threading utilizzino funzioni globali.
hkBattousai,

9

A meno che uno non desideri una funzione separata nei namespace globali, possiamo usare le funzioni lambda per creare thread.

Uno dei principali vantaggi della creazione di thread tramite lambda è che non è necessario passare parametri locali come elenco di argomenti. Possiamo usare lo stesso elenco di acquisizione e la proprietà di chiusura di lambda si occuperà del ciclo di vita.

Ecco un codice di esempio

int main() {
    int localVariable = 100;

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

    th.join();

    return 0;
}

Di gran lunga, ho scoperto che C ++ lambdas è il modo migliore per creare thread specialmente per funzioni thread più semplici


8

Dipende in gran parte dalla libreria che decidi di utilizzare. Ad esempio, se si utilizza la libreria wxWidgets, la creazione di un thread sarebbe simile alla seguente:

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

Se il tuo thread principale chiama il metodo CreateThread, creerai un nuovo thread che inizierà l'esecuzione del codice nel tuo metodo "Entry". Nella maggior parte dei casi dovrai mantenere un riferimento al thread per unirlo o interromperlo. Maggiori informazioni qui: documentazione di wxThread


1
Hai dimenticato di eliminare il thread. Forse intendevi creare un thread distaccato ?
aib

1
Sì, ho estratto il codice da un codice su cui sto attualmente lavorando e, naturalmente, il riferimento al thread è memorizzato da qualche parte per unirlo, interromperlo ed eliminarlo in seguito. Grazie. :)
LorenzCK,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.