是否可以在不进行任何克隆的情况下与线程共享数据? [英] Is it possible to share data with threads without any cloning?

查看:86
本文介绍了是否可以在不进行任何克隆的情况下与线程共享数据?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

当我将工作委派给线程时,我经常拥有一段数据,这些数据将超过所有线程的寿命,例如以下示例中的numbers:

use std::thread;

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

    let thread_a = thread::spawn(|| println!("{}", numbers.len()));
    let thread_b = thread::spawn(|| println!("{}", numbers.len()));

    thread_a.join().unwrap();
    thread_b.join().unwrap();
}

它没有在任何地方进行修改,并且由于join的存在,可以保证使用它完成线程.但是,Rust的借阅检查器无法分辨:

 error[E0373]: closure may outlive the current function, but it borrows `numbers`, which is owned by the current function
 --> src/main.rs:6:34
  |
6 |     let thread_a = thread::spawn(|| println!("{}", numbers.len()));
  |                                  ^^                ------- `numbers` is borrowed here
  |                                  |
  |                                  may outlive borrowed value `numbers`
  |
note: function requires argument type to outlive `'static`
 --> src/main.rs:6:20
  |
6 |     let thread_a = thread::spawn(|| println!("{}", numbers.len()));
  |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
help: to force the closure to take ownership of `numbers` (and any other referenced variables), use the `move` keyword
  |
6 |     let thread_a = thread::spawn(move || println!("{}", numbers.len()));
  |                                  ^^^^^^^
 

到目前为止,我所看到的解决方案都涉及克隆数据(或克隆数据的Arc).可以不进行任何克隆而做到吗?

解决方案

您可能有一个错误的主意:克隆Arc只是增加一个参考计数器并制作一个指针的副本.它不执行任何其他分配.当然,创建Arc涉及分配,但是随后,您已经在分配以便构造Vec,因此,再增加一个固定大小的分配不太可能受到损害.

如果您真正需要的只是长度,您可以只计算线程封闭的外部并将其存储在变量中; usize跨越线程边界没有问题.

问题在于,编译器无法通过使用join()推断给定线程的寿命有限……它甚至没有尝试.

在Rust 1.0之前,存在 一个thread::scoped构造函数,该构造函数允许您传入非'static引用,但由于内存安全性问题而不得不使其不稳定.请参见如何将对堆栈变量的引用传递给线程?.

When I'm delegating work to threads I often have a piece of data that will outlive all of the threads, such as numbers in the following example:

use std::thread;

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

    let thread_a = thread::spawn(|| println!("{}", numbers.len()));
    let thread_b = thread::spawn(|| println!("{}", numbers.len()));

    thread_a.join().unwrap();
    thread_b.join().unwrap();
}

It's not modified anywhere, and because of the joins, it's guaranteed that the threads are done using it. However, Rust's borrow checker is not able to tell:

error[E0373]: closure may outlive the current function, but it borrows `numbers`, which is owned by the current function
 --> src/main.rs:6:34
  |
6 |     let thread_a = thread::spawn(|| println!("{}", numbers.len()));
  |                                  ^^                ------- `numbers` is borrowed here
  |                                  |
  |                                  may outlive borrowed value `numbers`
  |
note: function requires argument type to outlive `'static`
 --> src/main.rs:6:20
  |
6 |     let thread_a = thread::spawn(|| println!("{}", numbers.len()));
  |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
help: to force the closure to take ownership of `numbers` (and any other referenced variables), use the `move` keyword
  |
6 |     let thread_a = thread::spawn(move || println!("{}", numbers.len()));
  |                                  ^^^^^^^

The solutions I've seen so far all involve cloning the piece of data (or cloning an Arc of the data). Is it possible to do it without any cloning, though?

解决方案

You might have the wrong idea: cloning an Arc is just incrementing a reference counter and making a copy of a pointer; it doesn't perform any additional allocation. Of course, creating the Arc involves an allocation, but then, you're already allocating in order to construct the Vec, so one additional fixed-size allocation isn't likely to hurt.

If all you really need is the length, you can just compute that outside the thread's closure and store it in a variable; a usize has no problems crossing a thread boundary.

The issue is that the compiler is unable to infer from the use of join() that a given thread is bound to a limited lifetime... it doesn't even try.

Before Rust 1.0, there was a thread::scoped constructor that allowed you to pass in non-'static references, but that had to be de-stabilised due to a memory safety issue. See How can I pass a reference to a stack variable to a thread? for alternatives.

这篇关于是否可以在不进行任何克隆的情况下与线程共享数据?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆