Entendendo como utilizar std::async em C++

Um exemplo com e sem std::async para notarmos as diferenças.


Entendendo como utilizar async em C++


std::async foi introduzido no C++11. Ele é um template de função que aceita funções de callback como argumento e potencialmente os executa de forma assíncrona.

Ou seja, utilizando-o você pode melhorar a velocidade de uma aplicação, pois o tempo de retorno é usado de forma paralela.

Podemos criar std::async com 3 políticas de inicialização diferentes:

  • std::launch::async - Ele garante o comportamento assíncrono, ou seja, a função passada será executada em uma thread separada.
  • std::launch::deferred - Comportamento não assíncrono, a função será chamada quando outro thread chamar get() no futuro para acessar o estado compartilhado.
  • std::launch::async | std::launch::deferred - É o comportamento padrão. Com esta política de inicialização, ele pode ser executado de forma assíncrona ou não.

Se não especificarmos uma política de inicialização, std::launch::async | std::launch::deferred será utilizado.

No exemplo desse artigo vamos utilizar std::launch::async.

Podemos passar qualquer tipo de função de callback em std::async:

  • Ponteiro de Função
  • Objeto de Função(functors)
  • Função Lambda


Exemplo de uso

Suponhamos que estamos desenvolvendo uma aplicação que precisa consumir 2 APIs diferentes e os dados retornados dessas APIs precisamos concatenar com dados que possuímos para gerar um novo nome.

Sem usar std::async faríamos mais ou menos assim:

Vamos ustilizar std::chrono para criar um sleep para dar impressão que seria o tempo de demora do retorno dos dados.

SEM std::async

vim exemplo-sem-async.cpp

#include <iostream>
#include <string>
#include <chrono>
#include <thread>

// Obter os dados da 1º API, tem um tempo de 2 segundos
std::string get_first_data_api(std::string ret_data){
  // sleep de 2 segundos
  std::this_thread::sleep_for( std::chrono::seconds(2));
  return "FIRST_" + ret_data;
}


// Obter os dados da 2º API, tem também um tempo de 2 segundos
std::string get_second_data_api(std::string ret_data){
  // sleep de 2 segundos
  std::this_thread::sleep_for( std::chrono::seconds(2));
  return "SECOND_" + ret_data;
}

int main(){
  // Obtém o início do tempo para a contagem
  std::chrono::system_clock::time_point start = 
    std::chrono::system_clock::now();

  // Solicita os dados da primeira API
  std::string first_api = get_first_data_api("NUMERO_UM");

  // Solicita os dados da segunda API
  std::string second_api = get_second_data_api("NUMERO_DOIS");

  // Obtém o final do tempo
  auto end = std::chrono::system_clock::now();

  // Diferença do tempo para saber quanto tempo foi gasto
  auto diff = 
    std::chrono::duration_cast<std::chrono::seconds>(end - start).count();

  // Imprime o temp gasto
  std::cout << "Tempo gasto para a requisição: " << 
    diff << " Segundos" << '\n';

  // Imprime a saída
  std::cout <<
    "Primeira API: "<< first_api <<
    "\nSegunda  API: " << second_api << '\n';

  return 0;
}

A saída desse exemplo será 4 segundos, pois será somado o tempo da 1º API(2 segundos) mais o tempo da 2º API(2 segundos).

Tempo gasto para a requisição: 4 Segundos
Primeira API: FIRST_NUMERO_UM
Segunda  API: SECOND_NUMERO_DOIS

Ou seja, um foi executado após o outro.

Agora vamos utilizar o mesmo código, mas vamos incluir somente TRÊS linhas para usar o std::async:

  • 1. Vamos incluir o cabeçalho: #include <future>
  • 2. Após o comentário: // Solicita os dados da primeira API, vamos alterar a linha para essa linha:
std::future<std::string> result_api_one = std::async(std::launch::async, get_first_data_api, "NUMERO_UM");
  • 3. E após a solicitação da segunda API vamos adicionar essa linha:
std::string first_api = result_api_one.get();

Perceba que estamos agora utilizando std::future para obter os dados via a função get().

COM std::async

vim exemplo-com-async.cpp

O código final com uso de std::async ficaria assim:

#include <iostream>
#include <string>
#include <chrono>
#include <thread>
#include <future>

std::string get_first_data_api(std::string ret_data){
  // sleep de 2 segundos
  std::this_thread::sleep_for( std::chrono::seconds(2));
  return "FIRST_" + ret_data;
}

std::string get_second_data_api(std::string ret_data){
  // sleep de 2 segundos
  std::this_thread::sleep_for( std::chrono::seconds(2));
  return "SECOND_" + ret_data;
}

int main(){
  // Obtém o início do tempo para a contagem
  std::chrono::system_clock::time_point start = 
    std::chrono::system_clock::now();

  // Solicita os dados da primeira API
  std::future<std::string> result_api_one = std::async(std::launch::async, get_first_data_api, "NUMERO_UM");

  // Solicita os dados da segunda API
  std::string second_api = get_second_data_api("NUMERO_DOIS");

  // Bloqueará até que os dados estejam disponíveis no objeto future<std::string>.
  std::string first_api = result_api_one.get();

  // Obtém o final do tempo
  auto end = std::chrono::system_clock::now();

  auto diff = 
    std::chrono::duration_cast<std::chrono::seconds>(end - start).count();

  std::cout << "Tempo gasto para a requisição: " << 
    diff << " Segundos" << '\n';

  // Imprime a saída
  std::cout <<
    "Primeira API: "<< first_api <<
    "\nSegunda  API: " << second_api << '\n';

  return 0;
}

Para compilar precisamos utilizar a flag: -pthread, exemplo:

g++ -pthread exemplo-com-async.cpp

Agora note que a saída foram gastos somente 2 segundos, ou seja, a primeira API foi executada de forma paralela à segunda API, a saída será:

Tempo gasto para a requisição: 2 Segundos
Primeira API: FIRST_NUMERO_UM
Segunda  API: SECOND_NUMERO_DOIS

Agora ficou mais fácil de entender, né?


Por hoje é só, são pequenas doses diárias que farão sempre nos manter antenado com o C++ !

Acompanhe o cpp::daily

Deseja aprender C++ e criar seus programas Gráficos e para Terminal com portabilidade para Linux e Windows?

Então se inscreva nos nossos Cursos de C++ Moderno . Você aprender criar:

Acesse o endereço:

https://terminalroot.com.br/cpp/ e saiba mais!


Referências


cpp cppdaily


Compartilhe


Nosso canal no Youtube

Inscreva-se


Marcos Oliveira

Marcos Oliveira

Desenvolvedor de software
https://github.com/terroo


Crie Aplicativos Gráficos para Linux e Windows com C++

Aprenda C++ Moderno e crie Games, Programas CLI, GUI e TUI de forma fácil.

Saiba Mais

Receba as novidades no seu e-mail!

Após cadastro e confirmação do e-mail, enviaremos semanalmente resumos e também sempre que houver novidades por aqui para que você mantenha-se atualizado!