Concorrência em C++: de threads a corrotinas e async/await

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

Introdução: por que a concorrência em C++ não é assustadora, mas sim interessante?

\n\n

Há muito tempo, na era dos processadores single-core, os programas eram executados estritamente de forma sequencial. Hoje, até mesmo seu smartphone tem 8 núcleos e os servidores, dezenas. Fazer todos esses núcleos trabalharem para você é onde reside a verdadeira magia do desempenho. E o C++, uma das linguagens mais poderosas e de baixo nível, nos dá todo um arsenal de ferramentas para isso. Mas como não se confundir? Threads, corrotinas (muitas vezes chamadas de "gorrotinas" por analogia com Go), async/await — o que escolher?

\n\n

Neste artigo, vamos organizar a concorrência moderna em C++ de forma clara. Você aprenderá quando usar threads clássicas e quando usar as leves corrotinas do C++20, e como o async/await simplifica a programação assíncrona. Vamos nessa!

\n\n

Clássico: multithreading com std::thread

\n\n

Vamos começar com o mais óbvio — as threads. std::thread é o acesso direto às threads do sistema operacional. Cada thread é um contexto de execução separado que pode ser executado em um núcleo de CPU diferente.

\n\n

Exemplo simples: cálculo paralelo

\n\n

Imagine que você precisa calcular a soma dos quadrados dos números de 1 a 10 milhões. Sequencialmente, é demorado. Vamos paralelizar:

\n\n
#include <iostream>\n#include <thread>\n#include <vector>\n#include <numeric>\n\nvoid sum_squares(int start, int end, long long& result) {\n    result = 0;\n    for (int i = start; i <= end; ++i) {\n        result += i * i;\n    }\n}\n\nint main() {\n    const int N = 10'000'000;\n    const int NUM_THREADS = 4;\n    std::vector<std::thread> threads;\n    std::vector<long long> results(NUM_THREADS, 0);\n    \n    int chunk = N / NUM_THREADS;\n    for (int i = 0; i < NUM_THREADS; ++i) {\n        int start = i * chunk + 1;\n        int end = (i == NUM_THREADS - 1) ? N : (i + 1) * chunk;\n        threads.emplace_back(sum_squares, start, end, std::ref(results[i]));\n    }\n    \n    for (auto& t : threads) {\n        t.join();\n    }\n    \n    long long total = std::accumulate(results.begin(), results.end(), 0LL);\n    std::cout << "Soma dos quadrados: " << total << std::endl;\n    return 0;\n}\n
\n\n

Aqui, criamos 4 threads, cada uma calcula sua parte do intervalo. Os resultados são coletados em um vetor. Tudo simples e previsível. Mas há nuances:

\n\n
    \n
  • Overhead: criar cada thread é caro (alocação de pilha, chamadas de sistema). Para milhares de tarefas pequenas, isso é ineficiente.
  • \n
  • Sincronização: se as threads acessam dados compartilhados, são necessários mutexes, que podem causar bloqueios e reduzir o desempenho.
  • \n
  • Quantidade limitada: o número de threads geralmente não deve exceder o número de núcleos da CPU (considerando hyper-threading).
  • \n
\n\n

Para cálculos pesados e longos, as threads são uma excelente escolha. Mas para operações de E/S (leitura de arquivos, requisições de rede), elas geralmente são excessivas. É aqui que entram as corrotinas.

\n\n

Revolução do C++20: corrotinas (gorrotinas)

\n\n

As corrotinas no C++20 não são apenas mais um recurso. É uma mudança fundamental na forma como escrevemos código assíncrono. Ao contrário das threads, as corrotinas são co-rotinas leves que podem suspender sua execução e retomá-la mais tarde, sem bloquear a thread do SO.

\n\n

Muitos desenvolvedores as chamam de "gorrotinas" devido à semelhança com as goroutines em Go, embora tecnicamente sejam conceitos diferentes. A principal vantagem: você pode executar centenas de milhares de corrotinas em uma única thread sem perda de desempenho.

\n\n

Como funciona?

\n\n

Uma corrotina é uma função que contém a palavra-chave co_await, co_return ou co_yield. O compilador a transforma automaticamente em uma máquina de estados. Aqui está um exemplo básico:

\n\n
#include <iostream>\n#include <coroutine>\n#incl                

Blogs

Book Recommendations