AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / coding / Perguntas / 79156647
Accepted
narumi
narumi
Asked: 2024-11-05 02:51:24 +0800 CST2024-11-05 02:51:24 +0800 CST 2024-11-05 02:51:24 +0800 CST

Por que `while let` em uma implementação Future aciona o Waker, mas `if let` não?

  • 772

Tenho trabalhado em uma implementação Future em Rust, e encontrei um comportamento que não entendo completamente. Especificamente, estou usando std::sync::mpsc::Receiverdentro do pollmétodo, e estou tentando decidir entre while lete if letpara receber mensagens.

Aqui está a versão simplificada do meu método de enquete:

fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Self::Output> {
    let this = self.get_mut();

    // Option 1: Using `while let`
    while let Ok(new_attributes) = this.receiver.try_recv() {
        // Process the message
    }

    // Option 2: Using `if let`
    if let Ok(new_attributes) = this.receiver.try_recv() {
        // Process the message
    }

    Poll::Pending
}

O que eu observei

Quando uso while let, tudo parece funcionar como esperado: o Future é re-polled quando novas mensagens chegam. No entanto, quando uso if let, o Future parece travar e nunca mais acorda, mesmo quando novas mensagens estão disponíveis.

Meu Entendimento

Eu sei que o Waker deve ser usado para notificar o executor de que o Future deve ser re-pesquisado. Dado que minha função de pesquisa sempre retorna Poll::Pending, eu esperaria que o Future continuasse sendo pesquisado repetidamente, a menos que o próprio processo pare. No entanto, não entendo por que while let garante que o Waker seja acionado corretamente, enquanto if let não parece fazer o mesmo.

Contexto adicional

O Futuro é gerado assim:

ctx.task_executor()
    .spawn_critical_blocking("transaction execution service", Box::pin(fut_struct));

A função spawn_critical_blocking funciona da seguinte maneira:

pub fn spawn_critical_blocking<F>(&self, name: &'static str, fut: F) -> JoinHandle<()>
where
    F: Future<Output = ()> + Send + 'static,
{
    self.spawn_critical_as(name, fut, TaskKind::Blocking)
}

E internamente:

fn spawn_critical_as<F>(
    &self,
    name: &'static str,
    fut: F,
    task_kind: TaskKind,
) -> JoinHandle<()>
where
    F: Future<Output = ()> + Send + 'static,
{
    // Wrapping the future and handling task errors
    let task = std::panic::AssertUnwindSafe(fut)
        .catch_unwind()
        .map_err(...);

    let task = async move {
        // Handling task shutdown and execution
        let task = pin!(task);
        let _ = select(on_shutdown, task).await;
    };

    self.spawn_on_rt(task, task_kind)
}

O spawn_on_rtmétodo então usa spawn_blocking.

Minha pergunta

  • Por que usar while let garante que o Waker seja acionado, mas usar if let
  • faz

não?

  • Qual é o mecanismo subjacente que causa essa diferença de comportamento?

Quaisquer informações ou explicações serão muito apreciadas!

asynchronous
  • 1 1 respostas
  • 68 Views

1 respostas

  • Voted
  1. Best Answer
    Finn Bear
    2024-11-05T03:16:20+08:002024-11-05T03:16:20+08:00

    Antes de responder às suas perguntas, gostaria de desafiar uma de suas suposições:

    Dado que minha função de pesquisa sempre retorna Poll::Pending, eu esperaria que ela Futurecontinuasse sendo pesquisada repetidamente

    Isso, embora tecnicamente possível, não é de se esperar. asyncTempos de execução normais (como tokio) tentam ser eficientes apenas sondando quando o progresso foi feito, conforme sinalizado usando o Wakertransmitido pelo Context.

    Observe que você nunca usa o Contextargumento which, a menos que seu future sempre retorne Poll::Readyimediatamente, é um erro, pois não há razão confiável para que sua pollfunção seja chamada novamente. (Tecnicamente, é possível que ela seja chamada novamente, porque asyncos tempos de execução podem criar futures espúrios poll, mas você nunca deve confiar nisso!)

    Por que usar while let garante que o Wakerseja acionado, mas usar if let não?

    Não há nada sobre while letisso que seja garantido para disparar oWaker . É por esse motivo que o código a seguir não funciona:

    use std::task::{Context, Poll};
    use std::pin::Pin;
    use std::future::Future;
    use std::sync::mpsc::{channel, Receiver};
    
    struct Demo{
        receiver: Receiver<()>,
    }
    
    impl Future for Demo {
        type Output = ();
        fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Self::Output> {
            let this = self.get_mut();
            println!("poll");
        
            // Option 1: Using `while let`
            while let Ok(_) = this.receiver.try_recv() {
                println!("processing");
                // Process the message
            }
        
            Poll::Pending
        }
    }
    
    #[tokio::main]
    pub async fn main() {
        let (sender, receiver) = channel();
        let demo = Demo{receiver};
        let thread = std::thread::spawn(move || {
            sender.send(()).unwrap();
        });
        println!("before");
        demo.await;
        println!("after");
        thread.join().unwrap();
    }
    

    Saída:

    before
    poll
    

    Qual é o mecanismo subjacente que causa essa diferença de comportamento?

    A única diferença entre if lete while leté que o último continuará a ler do canal se houver mais mensagens armazenadas em buffer. Uma explicação alternativa, mais plausível, para o que você está vendo é que while letpermite uma única invocação de pollpara processar várias mensagens sem fazer nada para agendar uma invocação subsequente depoll .

    Como consertar

    O principal problema com seu código é que você está tentando usar uma implementação de canal síncrono da biblioteca padrão em um asynccontexto; Eles são incompatíveis, e misturá-los dessa forma é incorreto e não confiável. Você deve mudar para um asynccanal baseado em - como tokio::sync::mpsc:

    use std::task::{Context, Poll};
    use std::pin::Pin;
    use std::future::Future;
    use tokio::sync::mpsc::{channel, Receiver};
    
    struct Demo{
        receiver: Receiver<()>,
    }
    
    impl Future for Demo {
        type Output = ();
        fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
            let this = self.get_mut();
            
            println!("poll");
        
            if let Poll::Ready(result) = this.receiver.poll_recv(cx) {
                if let Some(_message) = result {
                    println!("processed");
                } else {
                    println!("channel closed");
                }
                return Poll::Ready(());
            }
        
            Poll::Pending
        }
    }
    
    #[tokio::main]
    pub async fn main() {
        let (sender, receiver) = channel(10);
        let demo = Demo{receiver};
        let task = tokio::spawn(async move {
            sender.send(()).await.unwrap();
        });
        println!("before");
        demo.await;
        println!("after");
        task.await.unwrap();
    }
    

    Observe como receber no canal agora envolve uma pollchamada interna do tipo - que toma o Contextcomo argumento. Se não houver uma mensagem para receber no momento, o Wakerserá associado ao canal (de uma forma específica da implementação) de modo que pode fazer com que o future seja pesquisado quando uma mensagem chegar.

    Saída:

    before
    poll
    poll
    processsed
    after
    

    Observe que há duas pesquisas. Presumivelmente, a primeira não encontrou nenhuma mensagem para receber. O uso adequado da asyncpesquisa significou que o futuro foi pesquisado produtivamente novamente.

    • 5

relate perguntas

  • função assíncrona como tipo de valor hashmap

  • Assistir canal enferrujado

  • A vida útil da estrutura com campo do tipo retorno de chamada assíncrono em caixa deve sobreviver a 'estático

  • Maneira mais simples de atualizar um campo de texto com os resultados de uma tarefa assíncrona no JetPack Compose

  • É seguro usar RefCell em uma estrutura de dados acessada com algum bloqueio de granulação grossa?

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    Vue 3: Erro na criação "Identificador esperado, mas encontrado 'import'" [duplicado]

    • 1 respostas
  • Marko Smith

    Por que esse código Java simples e pequeno roda 30x mais rápido em todas as JVMs Graal, mas não em nenhuma JVM Oracle?

    • 1 respostas
  • Marko Smith

    Qual é o propósito de `enum class` com um tipo subjacente especificado, mas sem enumeradores?

    • 1 respostas
  • Marko Smith

    Como faço para corrigir um erro MODULE_NOT_FOUND para um módulo que não importei manualmente?

    • 6 respostas
  • Marko Smith

    `(expression, lvalue) = rvalue` é uma atribuição válida em C ou C++? Por que alguns compiladores aceitam/rejeitam isso?

    • 3 respostas
  • Marko Smith

    Quando devo usar um std::inplace_vector em vez de um std::vector?

    • 3 respostas
  • Marko Smith

    Um programa vazio que não faz nada em C++ precisa de um heap de 204 KB, mas não em C

    • 1 respostas
  • Marko Smith

    PowerBI atualmente quebrado com BigQuery: problema de driver Simba com atualização do Windows

    • 2 respostas
  • Marko Smith

    AdMob: MobileAds.initialize() - "java.lang.Integer não pode ser convertido em java.lang.String" para alguns dispositivos

    • 1 respostas
  • Marko Smith

    Estou tentando fazer o jogo pacman usando apenas o módulo Turtle Random e Math

    • 1 respostas
  • Martin Hope
    Aleksandr Dubinsky Por que a correspondência de padrões com o switch no InetAddress falha com 'não cobre todos os valores de entrada possíveis'? 2024-12-23 06:56:21 +0800 CST
  • Martin Hope
    Phillip Borge Por que esse código Java simples e pequeno roda 30x mais rápido em todas as JVMs Graal, mas não em nenhuma JVM Oracle? 2024-12-12 20:46:46 +0800 CST
  • Martin Hope
    Oodini Qual é o propósito de `enum class` com um tipo subjacente especificado, mas sem enumeradores? 2024-12-12 06:27:11 +0800 CST
  • Martin Hope
    sleeptightAnsiC `(expression, lvalue) = rvalue` é uma atribuição válida em C ou C++? Por que alguns compiladores aceitam/rejeitam isso? 2024-11-09 07:18:53 +0800 CST
  • Martin Hope
    The Mad Gamer Quando devo usar um std::inplace_vector em vez de um std::vector? 2024-10-29 23:01:00 +0800 CST
  • Martin Hope
    Chad Feller O ponto e vírgula agora é opcional em condicionais bash com [[ .. ]] na versão 5.2? 2024-10-21 05:50:33 +0800 CST
  • Martin Hope
    Wrench Por que um traço duplo (--) faz com que esta cláusula MariaDB seja avaliada como verdadeira? 2024-05-05 13:37:20 +0800 CST
  • Martin Hope
    Waket Zheng Por que `dict(id=1, **{'id': 2})` às vezes gera `KeyError: 'id'` em vez de um TypeError? 2024-05-04 14:19:19 +0800 CST
  • Martin Hope
    user924 AdMob: MobileAds.initialize() - "java.lang.Integer não pode ser convertido em java.lang.String" para alguns dispositivos 2024-03-20 03:12:31 +0800 CST
  • Martin Hope
    MarkB Por que o GCC gera código que executa condicionalmente uma implementação SIMD? 2024-02-17 06:17:14 +0800 CST

Hot tag

python javascript c++ c# java typescript sql reactjs html

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve