Entenda a abordagem de Rust para simultaneidade baseada no conceito de "simultaneidade destemida".

Simultaneidade é a capacidade de um programa executar várias tarefas simultaneamente no mesmo núcleo da CPU. Tarefas simultâneas são executadas e concluídas em tempo de sobreposição sem ordem especificada, ao contrário do paralelismo, onde várias tarefas ou subtarefas da mesma tarefa são executadas ao mesmo tempo em hardware com vários processadores.

Rust se destaca por seus recursos de desempenho e suporte para simultaneidade de forma segura e eficiente. A abordagem de Rust à simultaneidade é baseada no conceito de "simultaneidade sem medo", em que a linguagem visa facilitar a escrita segura código simultâneo por meio de seu sistema de propriedade e empréstimo que impõe regras estritas em tempo de compilação para evitar rastros de dados e garante memória segurança.

Entendendo a simultaneidade no Rust

Rust fornece várias primitivas de simultaneidade para escrever programas simultâneos, incluindo threads, passagem de mensagens, mutexes, tipos atômicos e async/await para programação assíncrona.

instagram viewer

Aqui está uma visão geral das primitivas de simultaneidade do Rust:

  1. Tópicos: A ferrugem fornece um padrão:: tópico módulo em sua biblioteca padrão para criar e gerenciar threads. Você pode gerar novos tópicos com o tópico:: spawn função. O tópico:: spawn recebe um fechamento contendo o código para execução. Você também pode executar threads que podem ser executadas em paralelo, e o Rust fornece primitivas de sincronização para coordenar sua execução. O verificador de empréstimos garante que as referências não levem a comportamentos inesperados.
  2. Passagem de Mensagem: o modelo de simultaneidade do Rust oferece suporte à passagem de mensagens entre threads. Você usará os canais implementados através do std:: sincronizar:: mpsc módulo para passagem de mensagens. Um canal consiste em um transmissor (Remetente) e um receptor (Receptor). Os threads podem enviar mensagens pelo transmissor e recebê-las pelo receptor. Isso fornece uma maneira segura e sincronizada de comunicação entre threads.
  3. Mutexes e tipos atômicos: Rust fornece primitivas de sincronização, incluindo mutexes (std:: sincronizar:: Mutex) e tipos atômicos (std:: sincronizar:: atômico), para garantir o acesso exclusivo ao compartilhamento de dados. Os mutexes permitem que vários threads acessem dados simultaneamente, evitando corridas de dados. Os tipos atômicos fornecem operações atômicas em dados compartilhados, como incrementar um contador, sem exigir bloqueio explícito.
  4. Assíncrono/Aguardando e Futuros: Ferrugem assíncrono/aguardam A sintaxe fornece funcionalidade para escrever código assíncrono que você pode executar simultaneamente. Os programas assíncronos lidam com eficiência com tarefas vinculadas a E/S, permitindo que os programas executem outras tarefas enquanto aguardam outras operações de E/S. ferrugem assíncrono/aguardam A sintaxe é baseada em futuros, e você pode alimentá-los com o async-std ou Tóquio bibliotecas de tempo de execução.

Os encadeamentos Rust são leves e a ausência de sobrecarga de tempo de execução os torna adequados para aplicativos de alto desempenho. As primitivas de simultaneidade do Rust integram-se perfeitamente com várias bibliotecas e estruturas para diferentes necessidades de simultaneidade.

Como usar Spawn Threads em Rust

Você vai usar o padrão:: tópico módulo para gerar threads. O std:: thread:: spawn A função permite que você crie um novo thread que será executado simultaneamente com o thread principal ou qualquer outro thread existente em seu programa.

Veja como você pode gerar um tópico com o std:: thread:: spawn função:

usar padrão:: thread;

fnprincipal() {
// Gera um novo thread
deixar thread_handle = thread:: spawn(|| {
// Código executado na nova thread vai aqui
println!("Olá do novo tópico!");
});

// Espera o thread gerado terminar
thread_handle.join().unwrap();

// Código executado na thread principal continua aqui
println!("Olá do tópico principal!");
}

O principal A função cria um novo thread com o tópico:: spawn função passando um encerramento contendo o código para a execução no thread (neste caso, o encerramento é uma função anônima). O fechamento imprime uma mensagem indicando que o novo segmento está em execução.

O juntar método no thread_handle permite que o thread principal aguarde até que o thread gerado conclua a execução. chamando juntar, a função garante que o encadeamento principal aguarde a conclusão do encadeamento gerado antes de continuar.

Você pode gerar vários threads e usar um loop ou qualquer outro Estrutura de controle de ferrugem para criar vários fechamentos e gerar threads para cada um.

usar padrão:: thread;

fnprincipal() {
deixar num_threads = 5;

deixarmudo thread_handles = você![];

para eu em0..num_threads {
deixar thread_handle = thread:: spawn(mover || {
println!("Olá do tópico {}", eu);
});
thread_handles.push (thread_handle);
}

para lidar em thread_handles {
handle.join().unwrap();
}

println!("Todos os tópicos terminados!");
}

O loop for gera cinco threads, cada uma atribuída a um identificador exclusivo eu com a variável de loop. Os fechamentos capturam o valor de eu com o mover palavra-chave para evitar questões de propriedade, e a thread_handles vetor armazena os segmentos para mais tarde no juntar laço.

Depois de gerar todos os threads, o principal função itera sobre o thread_handles vetor, chamadas juntar em cada identificador e aguarda a execução de todos os threads.

Passando mensagens pelos canais

Você pode passar mensagens por meio de threads com canais. Rust fornece funcionalidade para passagem de mensagens no std:: sincronizar:: mpsc módulo. Aqui, mpsc significa "produtor múltiplo, consumidor único" e permite a comunicação entre vários segmentos enviando e recebendo mensagens por meio de canais.

Veja como você implementa a passagem de mensagens por meio de canais de comunicação entre threads em seus programas:

usar std:: sincronizar:: mpsc;
usar padrão:: thread;

fnprincipal() {
// Cria um canal
deixar (remetente, receptor) = mpsc:: canal();

// Gera um thread
Tópico:: spawn(mover || {
// Envia uma mensagem pelo canal
remetente.enviar("Olá do tópico!").desembrulhar();
});

// Recebe a mensagem na thread principal
deixar mensagem_recebida = receiver.recv().unwrap();
println!("Mensagem recebida: {}", mensagem_recebida);
}

O principal função cria um canal com mpsc:: canal() que retorna um remetente e um receptor. O remetente envia mensagens para o receptor que recebe as mensagens. O principal a função prossegue para gerar threads e mover a propriedade do Remetente ao fechamento do fio. Dentro do fechamento da rosca, o remetente.enviar() função envia uma mensagem através do canal.

O receiver.recv() A função recebe a mensagem interrompendo a execução até que o thread receba a mensagem. O principal A função imprime a mensagem no console após o recebimento bem-sucedido da mensagem.

Observe que enviar uma mensagem pelo canal consome o remetente. Se você precisar enviar mensagens de vários segmentos, poderá clonar o remetente com o remetente.clone() função.

Além disso, o mpsc O módulo fornece outros métodos como try_recv(), cujo não-bloqueio tenta receber uma mensagem, e iter(), que cria um iterador sobre as mensagens recebidas.

A passagem de mensagens por meio de canais fornece uma maneira segura e conveniente de comunicação entre threads, evitando corridas de dados e garantindo a sincronização adequada.

O modelo de propriedade e empréstimo da Rust garante a segurança da memória

Rust combina propriedade, empréstimo e o verificador de empréstimo para fornecer uma estrutura de programação robusta, segura e simultânea.

O verificador de empréstimos atua como uma rede de segurança, detectando possíveis problemas em tempo de compilação, em vez de depender de verificações de tempo de execução ou coleta de lixo.