Contents

Uma introdução à programação assíncrona em Rust

A utilização de modelos tradicionais de programação síncrona freqüentemente resulta em gargalos de desempenho devido ao fato de que o programa deve aguardar a conclusão de operações mais lentas antes de continuar com as tarefas subsequentes, levando a uma alocação de recursos abaixo do ideal e a uma interface de usuário atrasada.

A programação assíncrona permite a criação de código não obstrutivo que emprega eficientemente os recursos do sistema. Através da utilização de programação assíncrona, é possível construir aplicações capazes de realizar múltiplas operações simultaneamente. A natureza vantajosa da programação assíncrona torna-se aparente ao gerenciar inúmeras consultas de rede ou processar quantidades substanciais de dados sem impedir a progressão do programa.

Programação Assíncrona em Rust

A utilização do paradigma de programação assíncrona do Rust permite a criação de código que opera de maneira desobstruída durante a execução de tarefas simultâneas, como manipulação de operações de entrada/saída, solicitações de rede e ações que requerem a alocação de recursos externos.

Existem vários caminhos para incorporar a programação assíncrona em aplicativos Rust, incluindo a utilização de recursos de linguagem, bibliotecas e o tempo de execução do Tokio.

Os aspectos mencionados acima do Rust, especificamente seu modelo de propriedade e primitivas de simultaneidade, como canais e bloqueios, facilitam a criação de programas simultâneos que são seguros e eficientes. Esses recursos podem ser empregados em conjunto com técnicas de programação assíncrona para construir sistemas escaláveis ​​que usam vários núcleos de unidade central de processamento (CPU).

Conceitos de programação assíncrona do Rust

Futuros servem como base para programação assíncrona em Rust, englobando computações que ainda não foram totalmente realizadas.

Um futuro representa uma operação assíncrona e seu resultado final. O método poll() de um futuro é utilizado para determinar seu estado atual de prontidão. Se o futuro exigir processamento adicional, Poll::Pending será retornado, indicando que a tarefa deve ser agendada posteriormente. Por outro lado, se o futuro for preparado, Poll::Ready junto com o resultado final será retornado.

A cadeia de ferramentas convencional do Rust compreende primitivas de E/S assíncronas, uma variante assíncrona de E/S de arquivo, conectividade de rede e mecanismos de cronometragem. Tais primitivas permitem a execução de operações de entrada/saída de forma desbloqueada, evitando assim a interrupção da operação de um programa devido ao atraso causado por tarefas de entrada/saída pendentes.

A utilização da sintaxe async/await possibilita a criação de código assíncrono que se assemelha ao código síncrono, facilitando assim a compreensão e manutenção do código.

/pt/images/password-passkey-information.jpg

A filosofia da Rust em relação à programação assíncrona prioriza segurança e eficiência. Isso é obtido por meio de suas regras de propriedade e empréstimo, que promovem a segurança da memória e atenuam os problemas comumente encontrados na programação simultânea. A utilização da sintaxe async/await e futures permite que os desenvolvedores conceituem e implementem facilmente fluxos de trabalho assíncronos. Além disso, existe a opção de empregar um tempo de execução de terceiros para gerenciar tarefas, resultando em desempenho otimizado.

A utilização desses recursos linguísticos, bibliotecas e tempo de execução permite a criação de código altamente eficiente. Essa estrutura oferece uma abordagem robusta e amigável para a construção de sistemas assíncronos, tornando-a uma seleção preferida para projetos que exigem gerenciamento eficiente de operações intensivas de E/S e alto paralelismo.

As versões mais recentes do Rust, incluindo 1.39 e versões subsequentes, não incorporam suporte para operações assíncronas em sua biblioteca padrão. Para utilizar a sintaxe async/await para gerenciar tarefas assíncronas, é necessário empregar um pacote de terceiros, como Tokio ou async-std.

Programação Assíncrona Com Tokio

Tokio é um runtime assíncrono avançado para a linguagem de programação Rust, oferecendo desempenho e escalabilidade excepcionais no desenvolvimento de aplicativos de alta potência por meio de seu suporte para programação assíncrona. Além disso, possui recursos que aumentam sua flexibilidade e modularidade.

O aspecto fundamental do Tokio está em seu mecanismo de agendamento e execução de tarefas assíncronas. Ao empregar a sintaxe async/await, pode-se criar um código assíncrono que promova o uso eficiente dos recursos do sistema e o processamento simultâneo de tarefas. O loop de eventos gerenciado pelo Tokio lida efetivamente com o agendamento de tarefas, levando à utilização otimizada dos núcleos da CPU e sobrecarga mínima associada à troca de contexto.

Os combinadores da Tokio facilitam a coordenação e composição de tarefas. A biblioteca oferece ferramentas robustas para sincronização e combinação de tarefas, incluindo a capacidade de aguardar várias tarefas com join , escolher a primeira tarefa concluída com select ,

Por favor, adicione a caixa tokio à seção de dependências em seu arquivo Cargo.toml.

 [dependencies]
tokio = { version = "1.9", features = ["full"] }
 

A utilização da sintaxe assíncrona/await em programas Rust juntamente com Tokio pode ser feita da seguinte forma:

 use tokio::time::sleep;
use std::time::Duration;

async fn hello_world() {
    println!("Hello, ");
    sleep(Duration::from_secs(1)).await;
    println!("World!");
}

#[tokio::main]
async fn main() {
    hello_world().await;
}
 

A função hello\\_world é uma função assíncrona que utiliza a palavra-chave await para interromper sua execução até que um futuro seja resolvido. Esta função exibe “Hello,“no console e, em seguida, faz uma pausa por um segundo usando a chamada de função Duration::from\\_secs(1). A palavra-chave await espera então que o futuro sleep seja concluído antes de imprimindo “Mundo!” no console.

/pt/images/result-from-running-the-hello_world-function.jpg

O objetivo principal do presente código é servir como ponto de entrada para o tempo de execução do Tokio, com a ajuda do atributo #[tokio::main]. Este atributo marca a função hello\\_world() como ponto de partida para a execução do programa assíncrono.

Atrasando Tarefas Com Tokio

A utilização de atrasos ou funções de agendamento para executar tarefas dentro de um período de tempo designado é uma ocorrência comum na programação assíncrona. O Tokio runtime oferece um meio de implementar temporizadores assíncronos e atrasos por meio do módulo tokio::time.

Para adiar a execução de uma tarefa dentro do tempo de execução do Tokio, siga estas etapas:

 use std::time::Duration;
use tokio::time::sleep;

async fn delayed_operation() {
    println!("Performing delayed operation...");
    sleep(Duration::from_secs(2)).await;
    println!("Delayed operation completed.");
}

#[tokio::main]
async fn main() {
    println!("Starting...");
    delayed_operation().await;
    println!("Finished.");
}
 

A função atraso\\_operação implementa um prolongamento temporal de dois segundos utilizando o método sleep. A função atraso\\_operação é caracterizada por ser assíncrona e, portanto, pode valer-se da palavra-chave await para suspender sua operação até que o tempo alocado tenha decorrido.

/pt/images/result-from-running-the-delayed-operation-function.jpg

Tratamento de erros em programas assíncronos

Lidar com erros em código Rust assíncrono requer a utilização do tipo Result, que permite propagar erros de maneira estruturada. O ? O operador é usado para correspondência de padrões no tipo Result, permitindo a implementação da lógica de tratamento de erros de maneira elegante e concisa.

 use tokio::fs::File;
use tokio::io;
use tokio::io::{AsyncReadExt};

async fn read_file_contents() -> io::Result<String> {
    let mut file = File::open("file.txt").await?;
    let mut contents = String::new();
    file.read_to_string(&mut contents).await?;
    Ok(contents)
}

async fn process_file() -> io::Result<()> {
    let contents = read_file_contents().await?;
    // Process the file contents
    Ok(())
}

#[tokio::main]
async fn main() {
    match process_file().await {
        Ok(()) => println!("File processed successfully."),
        Err(err) => eprintln!("Error processing file: {}", err),
    }
}
 

A função read\\_file\\_contents retorna um resultado que significa a probabilidade de ocorrência de um erro de entrada/saída (E/S). Através da utilização do ? subsequente a cada operação assíncrona, o tempo de execução do Tokio propagará quaisquer erros que possam ocorrer na pilha de chamada.

O objetivo principal do programa é processar os resultados de uma operação usando uma instrução condicional, que gera uma mensagem de acordo com o resultado da operação.

Reqwest usa programação assíncrona para operações HTTP

Várias caixas predominantes, como Reqwest, utilizam Tokio para oferecer operações HTTP assíncronas.

Tokio, uma estrutura de E/S assíncrona altamente eficiente para Python, em conjunto com Reqwest, permite a execução de várias solicitações HTTP sem prejudicar outros processos. Isso permite o manuseio de um grande número de conexões simultâneas, garantindo o gerenciamento eficiente dos recursos.