Ejemplo simple de subprocesos en C ++

391

¿Alguien puede publicar un ejemplo simple de iniciar dos subprocesos (orientados a objetos) en C ++.

Estoy buscando objetos de subprocesos reales de C ++ en los que pueda extender los métodos de ejecución (o algo similar) en lugar de llamar a una biblioteca de subprocesos de estilo C.

Dejé de lado cualquier solicitud específica del sistema operativo con la esperanza de que quien respondiera respondería con bibliotecas multiplataforma para usar. Solo estoy haciendo eso explícito ahora.

Zak
fuente

Respuestas:

561

Cree una función que desee que ejecute el hilo, por ejemplo:

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

Ahora cree el threadobjeto que finalmente invocará la función anterior de la siguiente manera:

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

(Necesitas #include <thread>acceder a la std::threadclase)

Los argumentos del constructor son la función que ejecutará el hilo, seguidos de los parámetros de la función. El hilo se inicia automáticamente en la construcción.

Si más adelante desea esperar a que se termine el hilo ejecutando la función, llame a:

t1.join(); 

(Unirse significa que el hilo que invocó el nuevo hilo esperará a que el nuevo hilo termine la ejecución, antes de que continúe su propia ejecución).


El código

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

Más información sobre std :: thread aquí

  • En GCC, compila con -std=c++0x -pthread.
  • Esto debería funcionar para cualquier sistema operativo, dado que su compilador admite esta característica (C ++ 11).
MasterMastic
fuente
44
@ Preza8 VS10 no admite muchas funciones en C ++ 11. Rosca de soporte VS12 y VS13.
jodag
3
En el comentario "Versiones de GCC por debajo de 4.7, use -std=c++0x(en lugar de -std=c++0x)" Creo que el segundo "c ++ 0x" debería ser "c ++ 11", pero eso no es posible cambiarlo porque la edición es demasiado pequeña.
zentrunix
1
@MasterMastic ¿Qué diferencia hace si en lugar de std :: thread t1 (task1, "Hello") usamos std :: thread t1 (& task1, "Hello"), pasando la referencia de la función? ¿Deberíamos declarar la función como un puntero en este caso?
user2452253
3
@ user2452253 Si pasa "tarea1", pasa un puntero a la función que desea ejecutar (lo cual es bueno). Si pasa "& task1", ​​pasa un puntero a un puntero de una función, y los resultados probablemente no serían tan bonitos y muy indefinidos (sería como tratar de ejecutar instrucciones aleatorias una tras otra. Con la probabilidad correcta podría abre la puerta al infierno). Realmente solo desea pasar el puntero de función (un puntero con el valor de la dirección donde comienza la función). Si eso no aclara las cosas, ¡hágamelo saber y la mejor de las suertes!
MasterMastic
2
@curiousguy Bueno, la forma correcta de hacerlo es pasar un puntero de la función que desea ejecutar. En este caso la función es task1. Entonces el puntero de función también se denota por task1. Pero gracias por traer esto a colación porque creo que estaba equivocado y que es equivalente a &task1, por lo que no importa qué forma que elija para escribir (si es así, supongo que puntero a puntero de la función es &&task1- que sería malo ) Cuestión relevante .
MasterMastic
80

Bueno, técnicamente cualquier objeto de este tipo terminará construyéndose sobre una biblioteca de subprocesos de estilo C porque C ++ solo especificó un std::threadmodelo de stock en c ++ 0x, que se acaba de clavar y aún no se ha implementado. El problema es un tanto sistémico, técnicamente el modelo de memoria c ++ existente no es lo suficientemente estricto como para permitir una semántica bien definida para todos los casos de "suceder antes". Hans Boehm escribió un artículo sobre el tema hace un tiempo y fue fundamental para elaborar el estándar c ++ 0x sobre el tema.

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

Dicho esto, hay varias bibliotecas C ++ de subprocesos multiplataforma que funcionan bien en la práctica. Los bloques de creación de subprocesos de Intel contienen un objeto tbb :: thread que se aproxima mucho al estándar c ++ 0x y Boost tiene una biblioteca boost :: thread que hace lo mismo.

http://www.threadingbuildingblocks.org/

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

Usando boost :: thread obtendrías algo como:

#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
fuente
8
El subproceso de impulso es excelente: ¡mi único problema era que no podía (cuando lo usé por última vez) acceder realmente al identificador de subproceso subyacente nativo, ya que era un miembro de la clase privada! Hay un montón de cosas en win32 para las que necesita el controlador de subproceso, por lo que lo modificamos para que el identificador sea público.
Orion Edwards
44
Otro problema con boost :: thread es que, según recuerdo, no tiene la libertad de establecer el tamaño de la pila del nuevo hilo, una característica que lamentablemente no se incluye en el estándar c ++ 0x.
Edward KMETT el
Ese código me da un impulso :: excepción no copiable para esta línea: thread thread_1 = thread (task1); Tal vez sea porque estoy usando una versión anterior (1.32).
Frank
¿task1 no se declaró en este ámbito?
Jake Gaston
20

También hay una biblioteca POSIX para sistemas operativos POSIX. Verifique la compatibilidad

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

}

compilar con -lpthread

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

Hohenheimsenberg
fuente
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
fuente
13

Al buscar un ejemplo de una clase C ++ que llama a uno de sus propios métodos de instancia en un nuevo hilo, surge esta pregunta, pero no pudimos usar ninguna de estas respuestas de esa manera. Aquí hay un ejemplo que hace eso:

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

Tenga en cuenta que este ejemplo no entra en mutex o bloqueo.

livingtech
fuente
2
Gracias por publicar esto. Tenga en cuenta que la forma general de invocar un hilo en el método de instancia es algo como esto: Foo f; std :: thread t (& Foo :: Ejecutar, & f, args ...); (donde Foo es una clase que tiene 'Run ()' como función miembro).
Jay Elston el
Gracias por publicar esto. En serio, no entiendo por qué todos los ejemplos de subprocesos utilizan funciones globales.
hkBattousai
9

A menos que uno quiera una función separada en los nombres globales, podemos usar funciones lambda para crear hilos.

Una de las principales ventajas de crear hilos usando lambda es que no necesitamos pasar parámetros locales como una lista de argumentos. Podemos usar la lista de captura para el mismo y la propiedad de cierre de lambda se encargará del ciclo de vida.

Aquí hay un código de muestra

int main() {
    int localVariable = 100;

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

    th.join();

    return 0;
}

De lejos, he encontrado que C ++ lambdas es la mejor manera de crear hilos especialmente para funciones de hilo más simples

Daksh
fuente
8

Depende en gran medida de la biblioteca que decida usar. Por ejemplo, si usa la biblioteca wxWidgets, la creación de un hilo se vería así:

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 su hilo principal llama al método CreateThread, creará un nuevo hilo que comenzará a ejecutar el código en su método de "Entrada". Tendrá que mantener una referencia al hilo en la mayoría de los casos para unirse o detenerlo. Más información aquí: documentación de wxThread

LorenzCK
fuente
1
Olvidaste eliminar el hilo. ¿Quizás quisiste crear un hilo separado ?
ab
1
Sí, extraje el código de algún código en el que estoy trabajando actualmente y, por supuesto, la referencia al hilo se almacena en algún lugar para unirlo, detenerlo y eliminarlo más adelante. Gracias. :)
LorenzCK