【问题标题】:Is it valid to wake a Rust future while it's being polled?在轮询时唤醒 Rust 未来是否有效?
【发布时间】:2020-01-29 19:16:57
【问题描述】:

我希望我的未来能够睡一个“框架”,这样其他工作就可以进行。这是这个想法的有效实现吗?

use std::future::Future;
use std::task::{Context, Poll};
use std::pin::Pin;

struct Yield {
    yielded: bool,
}

impl Future for Yield {
    type Output = ();

    fn poll(mut self: Pin<&mut Self>, ctx: &mut Context) -> Poll<()> {
        if self.yielded {
            Poll::Ready(())
        } else {
            self.yielded = true;

            // This is the part I'm concerned about
            ctx.waker().wake_by_ref();

            Poll::Pending
        }
    }
}

具体来说,我担心的是,如果在投票返回Pending 之前进行了wake_by_ref 调用,上下文将不会“注意到”它。 poll 的接口契约是否保证此任务在以这种方式执行时会立即重新轮询?

【问题讨论】:

    标签: asynchronous rust future


    【解决方案1】:

    TL;DR:您的代码是有效的。

    根据唤醒者的合同,它必须再次轮询你的未来。否则,Future::poll 调用与实际执行某些工作的未来对应方之间可能存在竞争条件。

    我们来看一个例子:

    impl Future for Foo {
        type Output = ();
        fn poll(self: Pin<&mut Self>, ctx: &mut Context) -> Poll<()> {
            let result = communicate_with_worker(ctx); // returns false
    
            // <-- Time point (1)
    
            return match result {
                true => Poll::Pending,
                false => Poll::Ready(()),
            };
        }
    }
    

    时间点(1),future 已经确定它还没有准备好,但是有可能轮询线程在这里暂停,并且工作线程被调度并完成了它的工作。 p>

    然后工作线程将调用唤醒器并请求再次轮询未来。如果唤醒器决定不再轮询未来,因为它现在正在轮询未来,那么唤醒器将永远不会再次收到唤醒请求。

    这意味着唤醒器可能会丢弃之前的唤醒请求 poll 被调用,但不允许丢弃唤醒请求 在未来的poll 通话期间出现。


    我唯一的问题是:为什么要将轮询时间重新安排一帧?

    由于您的实际工作必须在单独的线程中完成(而不是在 fn poll 内部),因此重新安排轮询没有任何意义。

    【讨论】:

    • 回答你的问题:这是一个数据加载器实现,它最初是一个 facebook 库,它“在异步帧的过程中收集一系列密钥,然后向 API 发出批处理请求”。在这种情况下,“工作”是“等待其他任务有机会在执行之前将它们的键添加到这个批处理中。在单线程执行器中更有意义;我仍在制定具体的设计。
    猜你喜欢
    • 2022-10-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-19
    • 1970-01-01
    • 2013-08-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多