如何按值将变量传递给另一个线程?

How to pass a variable by value to another thread?

提问人:Serge Rogatch 提问时间:3/17/2023 最后编辑:cafce25Serge Rogatch 更新时间:3/17/2023 访问量:245

问:

我需要在 Rust 中启动多个线程,每个线程都接收其工作 ID(包括 0 到 31 之间的整数)。如何在 Rust 中做到这一点?

这是我尝试过的:

struct PtrWrapper(*const u8);

unsafe impl Sync for PtrWrapper {}
unsafe impl Send for PtrWrapper {}

// ...

let data = &mmap[begin_offset..end_offset];

let n_workers = 32;
let total_bytes = data.len();
let bytes_per_worker = (total_bytes + n_workers - 1) / n_workers;
let page_size = 512;
let data_ptr = PtrWrapper(data.as_ptr());
if bytes_per_worker >= page_size {
    thread::scope(|scope| {
        for i in 0..n_workers {
            scope.spawn(|| {
                let _ = &data_ptr;
                let i_first = bytes_per_worker * i;
                let i_limit = cmp::min(total_bytes, bytes_per_worker * (i+1));
                for j in (i_first..i_limit).step_by(page_size) {
                    unsafe {
                        std::ptr::read_volatile(data_ptr.0.offset(j.try_into().unwrap()));
                    }
                }
            });
        }
    });
}

这给出了一个编译错误:

  error[E0373]: closure may outlive the current function, but it borrows `i`, which is owned by the current function
     --> src/lib.rs:477:41
      |
  475 |                     thread::scope(|scope| {
      |                                    ----- has type `&'1 Scope<'1, '_>`
  476 |                         for i in 0..n_workers {
  477 |                             scope.spawn(|| {
      |                                         ^^ may outlive borrowed value `i`
  478 |                                 let _ = &data_ptr;
  479 |                                 let i_first = bytes_per_worker * i;
      |                                                                  - `i` is borrowed here
      |
  note: function requires argument type to outlive `'1`
     --> src/lib.rs:477:29
      |
  477 | / ...                   scope.spawn(|| {
  478 | | ...                       let _ = &data_ptr;
  479 | | ...                       let i_first = bytes_per_worker * i;
  480 | | ...                       let i_limit = cmp::min(total_bytes, bytes_per_worker * (i+1));
  ...   |
  485 | | ...                       }
  486 | | ...                   });
      | |________________________^
  help: to force the closure to take ownership of `i` (and any other referenced variables), use the `move` keyword
      |
  477 |                             scope.spawn(move || {
      |                                         ++++
  
  For more information about this error, try `rustc --explain E0373`.
  error: could not compile `safetensors-python` due to previous error

我把其他变量移到了范围之外,这样 Rust 就知道对它们的引用会比线程更长。但是,我可以用变量做什么?i

多线程 Rust 闭包 borrow-checker 传递值

评论

0赞 Jonas Fassbender 3/17/2023
您是否尝试过编译器的建议并在您传递到的闭包前面添加?movescope.spawn
0赞 Serge Rogatch 3/17/2023
@JonasFassbender,似乎破坏了其他所有内容,因为据我所知,它从外部范围中删除了值。当然,我已经尝试过了。move
0赞 Jonas Fassbender 3/17/2023
move隐式复制实现的值,其中包括引用。如果我错了,请纠正我,但 AFAICT 因此是唯一未实现的类型,它被您的闭包捕获。如果可以作为参考,也许会起作用?CopyPointerWrapperdata_ptrCopydata_ptrmove
0赞 Masklinn 3/17/2023
@SergeRogatch意味着新线程拥有闭包关闭的每个值的所有权(以太复制类型或移动类型),这通常是必要的,因为线程是无作用域的,并且(就类型系统而言)可以比它们的创建者活得更久。通常,解决方案是使用需要在多个线程之间共享的类型,并将弧的克隆移动到每个线程中。moveCopyMovespawnArc
2赞 Masklinn 3/17/2023
我不得不说看起来很狡猾,不过,你有一群工人在世界上不关心地从内存映射中进行不同步的读取(实际上也什么都不做)。PtrWrapperlet _ = &data_ptr;

答:

1赞 cafce25 3/17/2023 #1

只是添加可能对您不起作用,因为闭包内部的唯一正确用途是编译器足够“聪明”,只能尝试将该部分移动到闭包中,这导致它尝试移动不允许的裸,因为指针没有实现,也可以通过提前引用并添加到闭包中来修复它:movedata_ptrdata_ptr.0*const u8SendSyncdata_ptrmove

// …
let data_ptr = PtrWrapper(data.as_ptr());
let data_ptr = &data_ptr;
if bytes_per_worker >= page_size {
    std::thread::scope(|scope| {
        for i in 0..n_workers {
            scope.spawn(move || {
                let i_first = bytes_per_worker * i;
                let i_limit = total_bytes.min(bytes_per_worker * (i+1));
                for j in (i_first..i_limit).step_by(page_size) {
                    unsafe {
                        std::ptr::read_volatile(data_ptr.0.offset(j.try_into().unwrap()));
                    }
                }
            });
        }
    });
}