Exemplo simples de encadeamento em C ++

391

Alguém pode postar um exemplo simples de iniciar dois threads (orientados a objetos) em C ++.

Estou procurando objetos de thread C ++ reais nos quais eu possa estender os métodos de execução (ou algo semelhante), em vez de chamar uma biblioteca de threads no estilo C.

Eu deixei de fora quaisquer solicitações específicas do SO na esperança de que quem respondesse respondesse com bibliotecas de plataforma cruzada para usar. Só estou deixando isso explícito agora.

Zak
fonte

Respostas:

561

Crie uma função que você deseja que o thread execute, por exemplo:

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

Agora crie o threadobjeto que finalmente chamará a função acima da seguinte maneira:

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

(Você precisa #include <thread>acessar a std::threadclasse)

Os argumentos do construtor são a função que o thread executará, seguido pelos parâmetros da função. O encadeamento é iniciado automaticamente na construção.

Se, mais tarde, você desejar aguardar o término da execução da função, chame:

t1.join(); 

(Associação significa que o encadeamento que chamou o novo encadeamento aguardará a conclusão do novo encadeamento antes de continuar sua própria execução).


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

Mais informações sobre std :: thread aqui

  • No GCC, compile com -std=c++0x -pthread.
  • Isso deve funcionar para qualquer sistema operacional, desde que o seu compilador suporte esse recurso (C ++ 11).
MasterMastic
fonte
4
O @ Preza8 VS10 não suporta muitos recursos no C ++ 11. Thread de suporte VS12 e VS13.
jodag
3
No comentário "Versões do GCC abaixo de 4.7, use -std=c++0x(em vez de -std=c++0x)" Eu acredito que o segundo "c ++ 0x" deveria ser "c ++ 11", mas não é possível alterar porque a edição é muito pequena.
Zentrunix
11
@MasterMastic Que diferença faz se, em vez de std :: thread t1 (task1, "Hello") usamos std :: thread t1 (& task1, "Hello"), passando a referência da função? Devemos declarar a função como ponteiro neste caso?
precisa saber é o seguinte
3
@ user2452253 Se passar "task1", ​​passa um ponteiro para a função que deseja executar (o que é bom). Se você passar "& task1", ​​passa um ponteiro para um ponteiro de uma função, e os resultados provavelmente não seriam tão bonitos e muito indefinidos (seria como tentar executar instruções aleatórias uma após a outra. Com a probabilidade certa, você pode abra a porta do inferno). Você realmente quer passar o ponteiro da função (um ponteiro com o valor do endereço onde a função começa). Se isso não esclarecer as coisas, me avise e boa sorte!
MasterMastic
2
@curiousguy Bem, a maneira certa de fazer isso é passar um ponteiro da função que você deseja executar. Neste caso, a função é task1. Portanto, o ponteiro da função também é indicado por task1. Mas obrigado por trazer isso à tona, porque eu acredito que estava errado e é equivalente a &task1, por isso não importa qual formulário você escolher escrever (se sim, acho que o ponteiro para o ponteiro da função é &&task1- isso seria ruim ) Pergunta relevante .
MasterMastic
80

Bem, tecnicamente, qualquer objeto desse tipo acabará sendo construído sobre uma biblioteca de encadeamentos no estilo C, porque o C ++ apenas especificou um std::threadmodelo de estoque em c ++ 0x, que foi pregado e ainda não foi implementado. O problema é um pouco sistêmico, tecnicamente o modelo de memória c ++ existente não é estrito o suficiente para permitir uma semântica bem definida para todos os casos 'acontecem antes'. Hans Boehm escreveu um artigo sobre o assunto há algum tempo e foi fundamental para elaborar o padrão c ++ 0x sobre o assunto.

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

Dito isto, existem várias bibliotecas C ++ de segmento de plataforma cruzada que funcionam bem na prática. Os blocos de construção de encadeamentos Intel contêm um objeto tbb :: thread que se aproxima do padrão c ++ 0x e o Boost possui uma biblioteca boost :: thread que faz o mesmo.

http://www.threadingbuildingblocks.org/

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

Usando boost :: thread, você obterá 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
fonte
8
O thread de impulso é ótimo - meu único problema era que você não podia (quando o usei pela última vez) acessar o identificador de segmento subjacente nativo, pois era um membro da classe privada! Há uma tonelada de coisas no win32 para as quais você precisa do manipulador de threads, então ajustamos para tornar o identificador público.
Orion Edwards
4
Outro problema com o boost :: thread é que, se bem me lembro, você não tem a liberdade de definir o tamanho da pilha do novo thread - um recurso que também não é lamentavelmente incluído no padrão c ++ 0x.
Edward KMETT 5/11
Esse código me fornece uma exceção boost :: noncopyable para esta linha: thread thread_1 = thread (task1); Talvez seja porque eu estou usando uma versão mais antiga (1.32).
Frank
task1 não foi declarado neste escopo?
Jake Gaston
20

Há também uma biblioteca POSIX para sistemas operacionais POSIX. Verifique a compatibilidade

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

}

compile com -lpthread

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

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

Ao procurar um exemplo de uma classe C ++ que chame um de seus próprios métodos de instância em um novo encadeamento, essa pergunta surge, mas não conseguimos usar nenhuma dessas respostas dessa maneira. Aqui está um exemplo que faz isso:

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

Observe que este exemplo não entra em mutex ou bloqueio.

livingtech
fonte
2
Obrigado por postar isso. Observe que a forma geral de chamar um thread no método de instância é algo como isto: Foo f; std :: thread t (& Foo :: Run, & f, args ...); (onde Foo é uma classe que possui 'Run ()' como uma função de membro).
Jay Elston
Obrigado por postar isso. Eu realmente não entendo por que todos os exemplos de threading usam funções globais.
hkBattousai
9

A menos que alguém queira uma função separada em namespacs globais, podemos usar funções lambda para criar threads.

Uma das principais vantagens da criação de encadeamento usando lambda é que não precisamos passar parâmetros locais como uma lista de argumentos. Podemos usar a lista de capturas para o mesmo e a propriedade de fechamento do lambda cuidará do ciclo de vida.

Aqui está um código de exemplo

int main() {
    int localVariable = 100;

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

    th.join();

    return 0;
}

De longe, eu achei as lambdas C ++ a melhor maneira de criar threads, especialmente para funções mais simples de threads

Daksh
fonte
8

Depende em grande parte da biblioteca que você decide usar. Por exemplo, se você usar a biblioteca wxWidgets, a criação de um thread ficaria assim:

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 o seu thread principal chamar o método CreateThread, você criará um novo thread que começará a executar o código no seu método "Entry". Na maioria dos casos, você precisará manter uma referência ao encadeamento para ingressar ou pará-lo. Mais informações aqui: documentação wxThread

LorenzCK
fonte
11
Você esqueceu de excluir o tópico. Talvez você quisesse criar um segmento desanexado ?
Aib
11
Sim, eu extraí o código de algum código no qual estou trabalhando atualmente e, é claro, a referência ao thread é armazenada em algum lugar para ingressar, parar e excluí-lo posteriormente. Obrigado. :)
LorenzCK