Manufaturação industrial
Internet das coisas industrial | Materiais industriais | Manutenção e reparo de equipamentos | Programação industrial |
home  MfgRobots >> Manufaturação industrial >  >> Industrial programming >> Linguagem C

Domine o Multithreading em C++: Programação Concorrente Eficiente com Pthreads

Multithreading é uma forma especializada de multitarefa. Em geral, existem dois tipos: baseada em processos e baseada em threads. A multitarefa baseada em processos lida com a execução concorrente de programas distintos. A baseada em threads lida com a execução concorrente de partes do mesmo programa.

Um programa multithread contém duas ou mais partes que podem ser executadas concorrentemente. Cada parte é chamada de thread (linha de execução) e define um caminho de execução separado.

O C++ não possui suporte nativo para aplicações multithread até o padrão C++11. Antes disso, dependia-se inteiramente do sistema operacional. Este tutorial assume Linux e usa POSIX Threads (Pthreads), API disponível em sistemas Unix-like como FreeBSD, NetBSD, GNU/Linux, macOS e Solaris.

Criando Threads

A rotina a seguir cria uma thread POSIX:

#include <pthread.h>
pthread_create(thread, attr, start_routine, arg)

A função pthread_create cria uma nova thread e a torna executável. Pode ser chamada quantas vezes forem necessárias. Parâmetros:

Nº Parâmetro e Descrição
1 thread
Identificador opaco e único para a nova thread retornado pela sub-rotina.
2 attr
Objeto de atributo opaco para definir atributos da thread. Use NULL para valores padrão.
3 start_routine
Rotina C++ que a thread executará após ser criada.
4 arg
Argumento único passado para start_routine. Deve ser passado por referência como ponteiro void. Use NULL se nenhum argumento for necessário.

O número máximo de threads é dependente da implementação. Uma vez criadas, as threads são pares e podem criar outras threads. Não há hierarquia ou dependência implícita.

Encerrando Threads

Use a rotina a seguir para encerrar uma thread POSIX:

#include <pthread.h>
pthread_exit(status)

pthread_exit é usado para sair explicitamente de uma thread. Geralmente chamado após a thread completar seu trabalho.

Se main() terminar antes das threads criadas e sair com pthread_exit(), as outras threads continuarão executando. Caso contrário, serão terminadas automaticamente quando main() finalizar.

Exemplo: Criando 5 Threads

#include <iostream>
#include <cstdlib>
#include <pthread.h>

using namespace std;

#define NUM_THREADS 5

void *PrintHello(void *threadid) {
    long tid = (long)threadid;
    cout << "Hello World! Thread ID, " << tid << endl;
    pthread_exit(NULL);
}

int main() {
    pthread_t threads[NUM_THREADS];
    int rc, i;

    for (i = 0; i < NUM_THREADS; i++) {
        cout << "main(): criando thread, " << i << endl;
        rc = pthread_create(&threads[i], NULL, PrintHello, (void *)i);
        if (rc) {
            cout << "Erro: incapaz de criar thread, " << rc << endl;
            exit(-1);
        }
    }
    pthread_exit(NULL);
}

Compile com a biblioteca -lpthread:

$ g++ test.cpp -lpthread

Saída esperada (a ordem pode variar):

main(): criando thread, 0
main(): criando thread, 1
main(): criando thread, 2
main(): criando thread, 3
main(): criando 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 múltiplos argumentos via estrutura. Qualquer tipo de dado pode ser passado no callback da thread, pois ele aponta para void.

#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 = (struct thread_data *) threadarg;
    cout << "Thread ID: " << my_data->thread_id;
    cout << " Mensagem: " << my_data->message << endl;
    pthread_exit(NULL);
}

int main() {
    pthread_t threads[NUM_THREADS];
    struct thread_data td[NUM_THREADS];
    int rc, i;

    for (i = 0; i < NUM_THREADS; i++) {
        cout << "main(): criando thread, " << i << endl;
        td[i].thread_id = i;
        td[i].message = "Esta é a mensagem";
        rc = pthread_create(&threads[i], NULL, PrintHello, (void *)&td[i]);
        if (rc) {
            cout << "Erro: incapaz de criar thread, " << rc << endl;
            exit(-1);
        }
    }
    pthread_exit(NULL);
}

Unindo e Desanexando Threads

Duas rotinas para juntar ou desanexar threads:

pthread_join(threadid, status)
pthread_detach(threadid)

pthread_join() bloqueia a thread chamadora até que a threadid especificada termine. Apenas threads criadas como joinable podem ser unidas. Threads criadas como detached nunca podem ser unidas.

Exemplo: Aguardando Conclusão com pthread_join

#include <iostream>
#include <cstdlib>
#include <pthread.h>
#include <unistd.h>

using namespace std;

#define NUM_THREADS 5

void *wait(void *t) {
    long tid = (long)t;
    sleep(1);
    cout << "Dormindo na thread " << endl;
    cout << "Thread com id: " << tid << " ...saindo " << endl;
    pthread_exit(NULL);
}

int main() {
    int rc, i;
    pthread_t threads[NUM_THREADS];
    pthread_attr_t attr;
    void *status;

    // Inicializa e define thread como joinable
    pthread_attr_init(&attr);
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);

    for (i = 0; i < NUM_THREADS; i++) {
        cout << "main(): criando thread, " << i << endl;
        rc = pthread_create(&threads[i], &attr, wait, (void *)i);
        if (rc) {
            cout << "Erro: incapaz de criar thread, " << rc << endl;
            exit(-1);
        }
    }

    // Libera atributo e aguarda as outras threads
    pthread_attr_destroy(&attr);
    for (i = 0; i < NUM_THREADS; i++) {
        rc = pthread_join(threads[i], &status);
        if (rc) {
            cout << "Erro: incapaz de unir, " << rc << endl;
            exit(-1);
        }
        cout << "Main: thread id concluída: " << i;
        cout << " saindo com status: " << status << endl;
    }
    cout << "Main: programa saindo." << endl;
    pthread_exit(NULL);
}

Saída esperada:

main(): criando thread, 0
main(): criando thread, 1
main(): criando thread, 2
main(): criando thread, 3
main(): criando thread, 4
Dormindo na thread
Thread com id: 0 ...saindo
Dormindo na thread
Thread com id: 1 ...saindo
Dormindo na thread
Thread com id: 2 ...saindo
Dormindo na thread
Thread com id: 3 ...saindo
Dormindo na thread
Thread com id: 4 ...saindo
Main: thread id concluída:0 saindo com status:0
Main: thread id concluída:1 saindo com status:0
Main: thread id concluída:2 saindo com status:0
Main: thread id concluída:3 saindo com status:0
Main: thread id concluída:4 saindo com status:0
Main: programa saindo.

Com Pthreads, você tem controle total sobre o ciclo de vida das threads, permitindo construir aplicações concorrentes robustas e eficientes em C++.

Linguagem C

  1. Dominando Operadores em C++: Guia Completo com Exemplos Práticos
  2. Comentários em C++: Boas Práticas para Código Legível e Manutenível
  3. Dominando Templates de Classe em C++: Um Guia Prático
  4. Linguagem de Programação C++: Visão Geral e Principais Características
  5. Constantes e Literais em C++: Tipos, Sintaxe e Boas Práticas
  6. Guia Completo de Operadores em C++: Tipos, Uso e Boas Práticas
  7. Dominando Tipos Numéricos em C++: Guia Prático
  8. Entendendo Referências em C++: Conceitos-Chave e Diferenças de Ponteiros
  9. Dominando Templates em C++: Guia para Programação Genérica
  10. Domine o Multithreading em C++: Programação Concorrente Eficiente com Pthreads