Thread com função sem argumentos
1void foo() {
2 cout << "Corpo da função: " << this_thread::get_id() << endl;
3}
4
5void run () {
6 thread t1( foo );
7 // Algum processamento...
8 t1.join();
9}
Thread com função passando parâmetros por valor
1void fooByRef( int & a ) {
2 while( true ) {
3 cout << "O valor é " << a << endl;
4 this_thread::sleep_for( chrono::milliseconds( 1000 ) );
5 }
6}
7
8void run () {
9 int p = 9;
10 thread t2( fooByRef, std::ref( p ) ); // usar o wrapper ref para não passar por valor
11 // Algum processamento...
12 this_thread::sleep_for( chrono::milliseconds( 5000 ) );
13 p = 30;
14 t1.join();
15}
Thread com função passando parâmetros por referência
1void foo( int a, int b ) {
2}
3
4void run () {
5 int p = 9;
6 int q = 8;
7 thread t1( foo, p, q );
8 // Algum processamento...
9 t1.join();
10}
Thread com classe callable
1class CallableClass {
2 public:
3 void operator()() {
4 cout << "Corpo da função: " << this_thread::get_id() << endl;
5 }
6};
7
8void run() {
9 CallableClass obj;
10 thread t2( obj );
11 // Algum processamento...
12 t2.join();
13}
Thread com lambda
1// Sintaxe
2// [capture-list]( params ) /*mutable*/ /*constexpr*/ /*exception attribute*/ -> ret { body }
3// [capture-list]( params ) -> ret { body }
4// [capture-list]( params ) { body }
5// [capture-list]{ body }
6
7void run() {
8 thread t3( []{
9 cout << "Corpo da função: " << this_thread::get_id() << endl;
10 } );
11 // Algum processamento...
12 t3.join();
13}
Join, detach e terminate
- Uma thread representa um objeto de execução em nível de hardware e está num estado chamado joinable
- A opções no estado joinable são:
join
- introduz um ponto de sincronismo e bloqueia a execuçãodetach
- faz a execução continuar em modo independente
- Após esta escolha a thread se torna non joinable
- Se esquecer de realizar o join ou detach a função
std::terminate
será chamada pelo destrutor - Um programa com
std::terminate
é considerado não seguro.
Tratamento de exceção
Para evitar problemas caso uma exceção for lançada antes do join
use
RAII - Resource acquisition is initialization.
RAII:
- Construtor adquire os recursos
- Destrutor libera os recursos
Exemplo de código com problema:
1void run() {
2 thread t4( foo );
3 throw runtime_error( "Algum erro antes do join/detach" );
4 t4.join(); // esta linha nunca vai ser executada
5}
A classe ThreadGuard
fornece um auxiliar para garantir que o join
será
sempre executado quando o objeto sair do contexto.
1class ThreadGuard {
2 thread & mThread;
3
4 public:
5 // garantir que não haverá conversões implícitas
6 explicit ThreadGuard( thread & t ) : mThread( t ) { }
7
8 // realiza o join pelo destrutor
9 ~ThreadGuard() { if( mThread.joinable() ) { mThread.join(); } }
10
11 // não permite o uso dos contrutores de cópia e atribuição
12 ThreadGuard( const ThreadGuard & ) = delete;
13 ThreadGuard & operator= ( const ThreadGuard & ) = delete;
14};
15
16void run() {
17 thread t4( foo );
18 ThreadGuard tg( t4 ); // quando sair do escopo o join será chamado
19 throw runtime_error( "Este erro não afeta a thread acima" );
20}
Transferindo a posse das threads
1void foo() { }
2void bar() { }
3
4void run() {
5 thread t1( foo );
6 // thread t2 = t1; // Se fizer isso vai gerar erro de compilação
7
8 // usar move para transferir a posse da thread
9 thread t2 = std::move( t1 ); // t1 não é mais dono da thread
10 t1 = thread( bar ); // t1 tem nova thread
11
12 t1.join();
13 t2.join();
14}
thread::get_id()
- retorna um id único para cada thread ativa em execução
- retorna 0 para todas as threads não ativas
1void run() {
2 thread t1( foo );
3 thread t2( foo );
4 thread t3;
5
6 cout << t1.get_id() << endl; // 1234
7 cout << t2.get_id() << endl; // 5678
8 cout << t3.get_id() << endl; // 0
9
10 t1.join();
11 t2.join();
12
13 cout << t1.get_id() << endl; // 0
14 cout << t2.get_id() << endl; // 0
15}
sleep_for
std::this_thread::yield()
- retorna a fatia de tempo
- reinsere a thread na fila de execução
std:🧵:hardware_concurrency()
- retorna o número de thread concorrentes suportado pela implementação do sistema
- considere este valor apenas como uma dica
- é usualmente o número de cores lógicos