为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

如何将对堆栈变量的引用传递给线程?我正在编写一个WebSocket服务器,其中一个Web客户端连接到一个多线程计算机AI上下棋。WebSocket服务器希望传递一个Logger对象进入人工智能代码。这个Logger对象将把日志行从AI排到Web客户端。这个Logger必须包含对客户端连接的引用。我对生命如何与线程交互感到困惑。我用一个Wrapper由类型参数化的结构。这个run_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 });}这个wrapper参数存在于堆栈中,且其生存期不会延长。run_thread的堆栈框架,即使在堆栈帧结束之前线程将被连接。我可以从堆栈中复制值: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() {     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 });}其结果是:error: `v` does not live long enough   --> src/main.rs:22:32    | 22 |     run_thread(Wrapper { val: &v });    |                                ^ does not live long enough 23 | }    | - borrowed value only lives until here    |    = note: borrowed value must be valid for the static lifetime...在我真正的程序中,看起来两个Logger连接对象必须放置在Arc包装纸。客户端被要求将连接放在Arc当代码被并行化时,代码是库内部的。这尤其令人讨厌,因为连接的生存期肯定大于工作线程的生存期。我错过了什么吗?
查看完整描述

2 回答

?
隔江千里

TA贡献1906条经验 获得超10个赞

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

幸运的是,我们并不局限于标准库。至少有两个箱子作用域螺纹-保证在某个作用域结束之前退出的线程。这可以确保堆栈变量在线程的整个期间都可用:

还有一些板条箱可以抽象出“线程”的底层细节,但允许您完成目标:

下面是每一个例子。每个示例生成大量线程,并在没有锁定、没有锁定的情况下对本地向量进行变异。Arc也没有克隆。请注意,该突变具有sleep调用以帮助验证这些呼叫是并行进行的。

您可以扩展这些示例,以共享对实现的任何类型的引用。Sync,如Mutex或者Atomic*..然而,使用这些将引入锁定。

范围-线程池

use scoped_threadpool::Pool; // 0.1.9use std::{thread, time::Duration};fn main() {
    let mut vec = vec![1, 2, 3, 4, 5];
    let mut pool = Pool::new(vec.len() as u32);

    pool.scoped(|scoped| {
        for e in &mut vec {
            scoped.execute(move || {
                thread::sleep(Duration::from_secs(1));
                *e += 1;
            });
        }
    });

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

横梁

use crossbeam; // 0.6.0use 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.3use 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);}

客户端必须将连接放在Arc当代码被并行化时,代码是库内部的。

也许你能更好地隐藏你的并行性?您能否接受记录器,然后将其包装在Arc / Mutex在把它交给你的线程之前?


查看完整回答
反对 回复 2019-06-25
?
慕森王

TA贡献1777条经验 获得超3个赞

我的解决办法是Logger实施Clone,并有一个类型为Arc<Mutex<Connection>>..然后,用户可以将记录器的一个克隆传递给线程代码。用户无法转移Connection对于线程代码(用户需要它用于其他目的),因此我认为线程代码不可能方便地执行Arc并代表用户进行装箱。 

查看完整回答
反对 回复 2019-06-25
  • 2 回答
  • 0 关注
  • 517 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信