【问题标题】:Run background work and return "cancellation"-closure运行后台工作并返回“取消”-关闭
【发布时间】:2021-08-22 17:12:15
【问题描述】:

我对 Rust 很陌生,所以这可能是一个相当初学者的问题:假设我想启动一些在后台运行的异步操作,并且我想通过一个函数调用来停止它。所需的 API 如下所示:

let stop = show_loadbar("loading some data...").await;
// load data...
stop().await;

我的代码:

pub fn show_loadbar(text: &str) -> Box<dyn FnOnce() -> Box<dyn Future<Output=()>>>
{
    let (sender, receiver) = channel::bounded::<()>(0);
    let display = task::spawn(async move {
        while receiver.try_recv().is_err() {
            // show loadbar: xxx.await;
        }
        // cleanup: yyy.await;
    });

    // return a function which stops the load bar
    Box::new(move || {
        Box::new(async {
            sender.send(()).await;
            display.await;
        })
    })
}

我玩了很多(创建一个结构而不是一个函数和一些组合),但最后,我总是得到这样的错误:

error[E0373]: async block may outlive the current function, but it borrows `sender`, which is owned by the current function
  --> src/terminal/loading.rs:23:24
   |
23 |           Box::new(async {
   |  ________________________^
24 | |             sender.send(()).await;
   | |             ------ `sender` is borrowed here
25 | |             display.await;
26 | |         })
   | |_________^ may outlive borrowed value `sender`

鉴于所描述的 API,是否有可能在 Rust 中实现这样的功能? 除此之外,Rust 的实现方式是什么?也许这个接口绝对不是 Rust 应该做的。

非常感谢

【问题讨论】:

    标签: asynchronous rust async-await background-task


    【解决方案1】:

    可以通过将async 更改为async move 来修复您看到的即时错误,以便它通过值而不是通过引用来捕获sender。但是尝试使用您的代码会发现更多问题:

    • 您不能(也可能不需要)等待 show_loadbar(),因为它本身不是异步的。
    • 固定盒装的未来以等待它。
    • 有界async_std 通道的容量不能为 0(如果给定 0,它会发生混乱);
    • 处理sender.send()返回的错误,例如通过打开它。
    • (可选)通过返回 impl FnOnce(...) 而不是 Box&lt;dyn FnOnce(...)&gt; 来删除外框。

    考虑到这些,代码如下所示:

    pub fn show_loadbar(_text: &str) -> impl FnOnce() -> Pin<Box<dyn Future<Output = ()>>> {
        let (sender, receiver) = channel::bounded::<()>(1);
        let display = task::spawn(async move {
            while receiver.try_recv().is_err() {
                // show loadbar: xxx.await;
            }
            // cleanup: yyy.await;
        });
    
        // return a function which stops the load bar
        || {
            Box::pin(async move {
                sender.send(()).await.unwrap();
                display.await;
            })
        }
    }
    
    // usage example:
    async fn run() {
        let cancel = show_loadbar("xxx");
        task::sleep(Duration::from_secs(1)).await;
        cancel().await;
    }
    
    fn main() {
        task::block_on(run());
    }
    

    【讨论】:

    • 谢谢,效果很好,我什至有机会学习/google std::pin::Pin(我以前不知道)。谢谢:)!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多