Multithread C++
Multitarefa é uma forma especializada de multitarefa e multitarefa é o recurso que permite que seu computador execute dois ou mais programas simultaneamente. Em geral, existem dois tipos de multitarefa:baseado em processo e baseado em thread.
A multitarefa baseada em processos lida com a execução simultânea de programas. A multitarefa baseada em thread lida com a execução simultânea de partes do mesmo programa.
Um programa multithread contém duas ou mais partes que podem ser executadas simultaneamente. Cada parte desse programa é chamada de thread e cada thread define um caminho de execução separado.
C++ não contém nenhum suporte interno para aplicativos multithread. Em vez disso, depende inteiramente do sistema operacional para fornecer esse recurso.
Este tutorial assume que você está trabalhando no sistema operacional Linux e vamos escrever um programa C++ multi-thread usando POSIX. POSIX Threads, ou Pthreads, fornece APIs que estão disponíveis em muitos sistemas POSIX do tipo Unix, como FreeBSD, NetBSD, GNU/Linux, Mac OS X e Solaris.
Criando tópicos
A rotina a seguir é usada para criar um thread POSIX -
#include <pthread.h> pthread_create (thread, attr, start_routine, arg)
Aqui, pthread_create cria um novo thread e o torna executável. Essa rotina pode ser chamada quantas vezes quiser de qualquer lugar dentro do seu código. Aqui está a descrição dos parâmetros -
Sr.Nº | Parâmetro e descrição |
---|---|
1 | tópico Um identificador único e opaco para o novo thread retornado pela sub-rotina. |
2 | atr Um objeto de atributo opaco que pode ser usado para definir atributos de thread. Você pode especificar um objeto de atributos de encadeamento ou NULL para os valores padrão. |
3 | start_routine A rotina C++ que o thread executará assim que for criado. |
4 | arg Um único argumento que pode ser passado para start_routine. Ele deve ser passado por referência como uma conversão de ponteiro do tipo void. NULL pode ser usado se nenhum argumento for passado. |
O número máximo de threads que podem ser criados por um processo depende da implementação. Uma vez criadas, as threads são peers e podem criar outras threads. Não há hierarquia implícita ou dependência entre threads.
Encerrando Tópicos
Existe a seguinte rotina que usamos para terminar um thread POSIX -
#include <pthread.h> pthread_exit (status)
Aqui pthread_exit é usado para sair explicitamente de um thread. Normalmente, a rotina pthread_exit() é chamada depois que um thread conclui seu trabalho e não precisa mais existir.
Se main() terminar antes das threads que criou e sair com pthread_exit(), as outras threads continuarão a ser executadas. Caso contrário, eles serão encerrados automaticamente quando main() terminar.
Exemplo
Este código de exemplo simples cria 5 threads com a rotina pthread_create(). Cada thread imprime um "Hello World!" mensagem e, em seguida, termina com uma chamada para pthread_exit().
#include <iostream> #include <cstdlib> #include <pthread.h> using namespace std; #define NUM_THREADS 5 void *PrintHello(void *threadid) { long tid; tid = (long)threadid; cout << "Hello World! Thread ID, " << tid << endl; pthread_exit(NULL); } int main () { pthread_t threads[NUM_THREADS]; int rc; int i; for( i = 0; i < NUM_THREADS; i++ ) { cout << "main() : creating thread, " << i << endl; rc = pthread_create(&threads[i], NULL, PrintHello, (void *)i); if (rc) { cout << "Error:unable to create thread," << rc << endl; exit(-1); } } pthread_exit(NULL); }
Compile o seguinte programa usando a biblioteca -lpthread da seguinte forma -
$gcc test.cpp -lpthread
Agora, execute seu programa que fornece a seguinte saída -
main() : creating thread, 0 main() : creating thread, 1 main() : creating thread, 2 main() : creating thread, 3 main() : creating thread, 4 Hello World! Thread ID, 0 Hello World! Thread ID, 1 Hello World! Thread ID, 2 Hello World! Thread ID, 3 Hello World! Thread ID, 4
Passando argumentos para threads
Este exemplo mostra como passar vários argumentos por meio de uma estrutura. Você pode passar qualquer tipo de dados em um retorno de chamada de thread porque aponta para void conforme explicado no exemplo a seguir -
#include <iostream> #include <cstdlib> #include <pthread.h> using namespace std; #define NUM_THREADS 5 struct thread_data { int thread_id; char *message; }; void *PrintHello(void *threadarg) { struct thread_data *my_data; my_data = (struct thread_data *) threadarg; cout << "Thread ID : " << my_data->thread_id ; cout << " Message : " << my_data->message << endl; pthread_exit(NULL); } int main () { pthread_t threads[NUM_THREADS]; struct thread_data td[NUM_THREADS]; int rc; int i; for( i = 0; i < NUM_THREADS; i++ ) { cout <<"main() : creating thread, " << i << endl; td[i].thread_id = i; td[i].message = "This is message"; rc = pthread_create(&threads[i], NULL, PrintHello, (void *)&td[i]); if (rc) { cout << "Error:unable to create thread," << rc << endl; exit(-1); } } pthread_exit(NULL); }
Quando o código acima é compilado e executado, ele produz o seguinte resultado -
main() : creating thread, 0 main() : creating thread, 1 main() : creating thread, 2 main() : creating thread, 3 main() : creating thread, 4 Thread ID : 3 Message : This is message Thread ID : 2 Message : This is message Thread ID : 0 Message : This is message Thread ID : 1 Message : This is message Thread ID : 4 Message : This is message
Juntar e desconectar tópicos
Existem duas rotinas que podemos usar para unir ou desanexar threads -
pthread_join (threadid, status) pthread_detach (threadid)
A sub-rotina pthread_join() bloqueia a thread de chamada até que a thread 'threadid' especificada termine. Quando um thread é criado, um de seus atributos define se ele pode ser unido ou desanexado. Somente threads que são criados como juntáveis podem ser unidos. Se um thread for criado como desanexado, ele nunca poderá ser unido.
Este exemplo demonstra como aguardar a conclusão do thread usando a rotina de junção Pthread.
#include <iostream> #include <cstdlib> #include <pthread.h> #include <unistd.h> using namespace std; #define NUM_THREADS 5 void *wait(void *t) { int i; long tid; tid = (long)t; sleep(1); cout << "Sleeping in thread " << endl; cout << "Thread with id : " << tid << " ...exiting " << endl; pthread_exit(NULL); } int main () { int rc; int i; pthread_t threads[NUM_THREADS]; pthread_attr_t attr; void *status; // Initialize and set thread joinable pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); for( i = 0; i < NUM_THREADS; i++ ) { cout << "main() : creating thread, " << i << endl; rc = pthread_create(&threads[i], &attr, wait, (void *)i ); if (rc) { cout << "Error:unable to create thread," << rc << endl; exit(-1); } } // free attribute and wait for the other threads pthread_attr_destroy(&attr); for( i = 0; i < NUM_THREADS; i++ ) { rc = pthread_join(threads[i], &status); if (rc) { cout << "Error:unable to join," << rc << endl; exit(-1); } cout << "Main: completed thread id :" << i ; cout << " exiting with status :" << status << endl; } cout << "Main: program exiting." << endl; pthread_exit(NULL); }
Quando o código acima é compilado e executado, ele produz o seguinte resultado -
main() : creating thread, 0 main() : creating thread, 1 main() : creating thread, 2 main() : creating thread, 3 main() : creating thread, 4 Sleeping in thread Thread with id : 0 .... exiting Sleeping in thread Thread with id : 1 .... exiting Sleeping in thread Thread with id : 2 .... exiting Sleeping in thread Thread with id : 3 .... exiting Sleeping in thread Thread with id : 4 .... exiting Main: completed thread id :0 exiting with status :0 Main: completed thread id :1 exiting with status :0 Main: completed thread id :2 exiting with status :0 Main: completed thread id :3 exiting with status :0 Main: completed thread id :4 exiting with status :0 Main: program exiting.
Linguagem C