Multithreading 如何将对堆栈变量的引用传递给线程?

Multithreading 如何将对堆栈变量的引用传递给线程?,multithreading,rust,reference,lifetime,Multithreading,Rust,Reference,Lifetime,我正在编写一个WebSocket服务器,其中一个web客户端连接起来,与一个多线程计算机AI下棋。WebSocket服务器希望将记录器对象传递到AI代码中。Logger对象将通过管道将日志行从AI传输到web客户端。记录器必须包含对客户端连接的引用 我对生命周期如何与线程交互感到困惑。我用类型参数化的包装器结构重现了这个问题。run\u thread函数尝试打开值并记录它 use std::fmt::Debug; use std::thread; struct Wrapper<T: De

我正在编写一个WebSocket服务器,其中一个web客户端连接起来,与一个多线程计算机AI下棋。WebSocket服务器希望将
记录器
对象传递到AI代码中。
Logger
对象将通过管道将日志行从AI传输到web客户端。
记录器
必须包含对客户端连接的引用

我对生命周期如何与线程交互感到困惑。我用类型参数化的
包装器
结构重现了这个问题。
run\u thread
函数尝试打开值并记录它

use std::fmt::Debug;
use std::thread;

struct Wrapper<T: Debug> {
    val: T,
}

fn run_thread<T: Debug>(wrapper: Wrapper<T>) {
    let thr = thread::spawn(move || {
        println!("{:?}", wrapper.val);
    });

    thr.join();
}

fn main() {
    run_thread(Wrapper::<i32> { val: -1 });
}
如果
T
是对我不想复制的大对象的引用,则此操作不起作用:

use std::fmt::Debug;
use std::thread;

struct Wrapper<T: Debug + Send> {
    val: T,
}

fn run_thread<T: Debug + Send + 'static>(wrapper: Wrapper<T>) {
    let thr = thread::spawn(move || {
        println!("{:?}", wrapper.val);
    });

    thr.join();
}

fn main() {
    let mut v = Vec::new();
    for i in 0..1000 {
        v.push(i);
    }

    run_thread(Wrapper { val: &v });
}
我能想到的唯一解决办法是使用

use std::fmt::Debug;
use std::sync::Arc;
use std::thread;

struct Wrapper<T: Debug + Send + Sync + 'static> {
    arc_val: Arc<T>,
}

fn run_thread<T: Debug + Send + Sync + 'static>(wrapper: &Wrapper<T>) {
    let arc_val = wrapper.arc_val.clone();
    let thr = thread::spawn(move || {
        println!("{:?}", *arc_val);
    });

    thr.join();
}

fn main() {
    let mut v = Vec::new();
    for i in 0..1000 {
        v.push(i);
    }

    let w = Wrapper { arc_val: Arc::new(v) };
    run_thread(&w);

    println!("{}", (*w.arc_val)[0]);
}
使用std::fmt::Debug;
使用std::sync::Arc;
使用std::线程;
结构包装器(包装器:&包装器){
让arc_val=wrapper.arc_val.clone();
让thr=thread::spawn(移动| |){
println!(“{:?}”,*arc_val);
});
thr.join();
}
fn main(){
让mut v=Vec::new();
对于0..1000中的i{
v、 推(i);
}
设w=Wrapper{arc_val:arc::new(v)};
运行线程(&w);
println!({},(*w.arc_val)[0]);
}
在我的实际程序中,似乎
记录器
和连接对象都必须放置在
包装器中。当代码并行化在库内部时,客户端被要求在
中装箱连接,这似乎很烦人。这尤其令人讨厌,因为连接的生存期保证大于工作线程的生存期


我错过了什么吗?

标准库中的线程支持允许创建的线程比创建它们的线程寿命长;那是件好事!但是,如果要将堆栈分配变量的引用传递给其中一个线程,则无法保证该变量在线程执行时仍然有效。在其他语言中,这将允许线程访问无效内存,从而产生一堆内存安全问题

幸运的是,我们并不局限于标准库。至少有两个板条箱提供作用域线程,即保证在特定作用域结束之前退出的线程。这些可以确保堆栈变量在线程的整个持续时间内都可用:

  • (遗产)
还有一些板条箱可以抽象出“线程”的低级细节,但允许您实现目标:

下面是每种方法的示例。每个示例都会生成大量线程,并在没有锁定、没有
Arc
、也没有克隆的情况下就地变异一个局部向量。请注意,突变有一个
sleep
调用来帮助验证调用是否并行发生

您可以扩展示例以共享对实现的任何类型的引用,例如
互斥体
原子*
。然而,使用这些将引入锁定

横梁 人造丝 当代码并行化的库内部连接时,客户端需要在
中框接连接


也许你能更好地隐藏你的平行性?您能否接受日志记录程序,然后将其包装在
Arc
/
互斥体中,然后再将其发送给您的线程?

非常感谢您的回复!我的解决方案是让
记录器
实现
克隆
,并有一个类型为
Arc
的字段。然后用户可以将记录器的克隆传递给线程代码。用户无法将
连接的所有权
转移到线程化代码(用户出于其他目的需要它),因此我认为线程化代码不可能方便地代表用户执行
Arc
和装箱。如果您试图传递的变量无法实现克隆/复制,您该怎么办?就像来自rusb的USB设备手柄crate@BrandonRos
Vec
不实现
Copy
,并且在这些代码示例中未使用
Clone
。这里提供的代码适用于此类类型。
use std::fmt::Debug;
use std::sync::Arc;
use std::thread;

struct Wrapper<T: Debug + Send + Sync + 'static> {
    arc_val: Arc<T>,
}

fn run_thread<T: Debug + Send + Sync + 'static>(wrapper: &Wrapper<T>) {
    let arc_val = wrapper.arc_val.clone();
    let thr = thread::spawn(move || {
        println!("{:?}", *arc_val);
    });

    thr.join();
}

fn main() {
    let mut v = Vec::new();
    for i in 0..1000 {
        v.push(i);
    }

    let w = Wrapper { arc_val: Arc::new(v) };
    run_thread(&w);

    println!("{}", (*w.arc_val)[0]);
}
use crossbeam; // 0.6.0
use std::{thread, time::Duration};

fn main() {
    let mut vec = vec![1, 2, 3, 4, 5];

    crossbeam::scope(|scope| {
        for e in &mut vec {
            scope.spawn(move |_| {
                thread::sleep(Duration::from_secs(1));
                *e += 1;
            });
        }
    })
    .expect("A child thread panicked");

    println!("{:?}", vec);
}
use rayon::iter::{IntoParallelRefMutIterator, ParallelIterator}; // 1.0.3
use std::{thread, time::Duration};

fn main() {
    let mut vec = vec![1, 2, 3, 4, 5];

    vec.par_iter_mut().for_each(|e| {
        thread::sleep(Duration::from_secs(1));
        *e += 1;
    });

    println!("{:?}", vec);
}