C ++ Multithreading
Multithreading é uma forma especializada de multitarefa e multitarefa é o recurso que permite ao computador executar dois ou mais programas simultaneamente. Em geral, existem dois tipos de multitarefa: baseada em processos e baseada em threads.
A multitarefa baseada em processo 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, ele 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-threaded usando POSIX. POSIX Threads ou Pthreads fornece API que está disponível em muitos sistemas POSIX do tipo Unix, como FreeBSD, NetBSD, GNU / Linux, Mac OS X e Solaris.
Criando Threads
A seguinte rotina é usada para criar um thread POSIX -
#include <pthread.h>
pthread_create (thread, attr, start_routine, arg)
Aqui, pthread_createcria um novo thread e o torna executável. Esta rotina pode ser chamada qualquer número de vezes de qualquer lugar dentro do seu código. Aqui está a descrição dos parâmetros -
Sr. Não | Parâmetro e Descrição |
---|---|
1 | thread Um identificador opaco e exclusivo para o novo encadeamento retornado pela sub-rotina. |
2 | attr Um objeto de atributo opaco que pode ser usado para definir atributos de thread. Você pode especificar um objeto de atributos de thread ou NULL para os valores padrão. |
3 | start_routine A rotina C ++ que o thread executará depois de criado. |
4 | arg Um único argumento que pode ser passado para start_routine. Deve ser passado por referência como um lançamento 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 criados, os threads são pares e podem criar outros threads. Não há hierarquia implícita ou dependência entre threads.
Encerrando Threads
Existe a seguinte rotina que usamos para encerrar 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 uma thread concluiu 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 em execução. Caso contrário, eles serão encerrados automaticamente quando main () terminar.
Example
Este código de exemplo simples cria 5 threads com a rotina pthread_create (). Cada thread imprime um "Hello World!" mensagem, e então 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 dá 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
Unindo e desanexando tópicos
Seguem-se duas rotinas que podemos usar para juntar ou separar threads -
pthread_join (threadid, status)
pthread_detach (threadid)
A sub-rotina pthread_join () bloqueia o thread de chamada até que o thread 'threadid' especificado termine. Quando um encadeamento é criado, um de seus atributos define se ele pode ser juntado ou separado. Apenas threads que são criados como uníveis podem ser unidos. Se um encadeamento for criado como separado, 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.