【问题标题】:Using thread unsafe values in an async block在异步块中使用线程不安全值
【发布时间】:2020-12-06 01:12:55
【问题描述】:

在这段代码 sn-p (playground link) 中,我们在两个线程之间进行了一些简单的通信。主线程(执行第二个async 块)将2 发送到async move 块中的线程2,后者接收它,添加自己的值,然后通过另一个通道将结果发送回主线程,主线程打印值。

线程 2 包含一些局部状态,thread_unsafe 变量,它既不是 Send 也不是 Sync,并通过 .await 维护。因此,我们正在创建的impl Future 对象本身既不是Send 也不是Sync,因此对pool.spawn_ok 的调用是一个编译错误。

但是,这似乎应该没问题。我理解为什么spawn_ok() 不能接受不是Send 的未来,我也理解为什么将异步块编译到状态机会导致结构包含非Send 值,但在这个例子我唯一想发送到另一个线程的是recvsend2。如何表示future只有在发送后才切换到非线程安全模式?

use std::rc::Rc;
use std::cell::RefCell;
use futures::channel::oneshot::channel;
use futures::executor::{ThreadPool, block_on};

fn main() {
    let pool = ThreadPool::new().unwrap();
    let (send, recv) = channel();
    let (send2, recv2) = channel();
    pool.spawn_ok(async move {
        let thread_unsafe = Rc::new(RefCell::new(40));
        let a = recv.await.unwrap();
        send2.send(a + *thread_unsafe.borrow()).unwrap();
    });
    let r = block_on(async {
        send.send(2).unwrap();
        recv2.await.unwrap()
    });
    println!("the answer is {}", r)
}

【问题讨论】:

    标签: multithreading rust future


    【解决方案1】:

    但在这个例子中,我唯一想发送到另一个线程的是recvsend2

    还有一个局部变量thread_unsafe 用于.await。由于.await 可以暂停一个异步函数,然后在另一个线程上恢复它,这可能会将thread_unsafe 发送到另一个线程,这是不允许的。

    【讨论】:

    • 这是spawn_ok 的属性吗?如,是否有一个执行器总是在它们开始的线程上执行任务,以便它可以执行线程不安全的未来?我想这至少可以在类型系统中表示为一个 Send 闭包,它返回一个非Send 未来来执行。
    • @MarioCarneiro 这是多线程执行器的属性。单线程执行器应该能够生成和执行非Send 未来就好了。
    • @MarioCarneiro:确实,例如smol::LocalExecutor 并不要求未来是Send
    • @rodrigo 但是那个执行器完全是单线程的,对吧?是否存在具有线程池类型设置的执行程序,其中作业被分配给各个线程,但作业一旦启动就不会从它们的线程中移出?
    • @MarioCarneiro:在 tokio 的 spawn_local 或 async_std 的 spawn_local 上掠夺:多线程执行器,但任务被固定到创建它的线程。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-11-16
    • 2021-11-06
    • 1970-01-01
    • 2011-04-06
    • 2016-06-08
    • 1970-01-01
    相关资源
    最近更新 更多