在Rust中的线程之间共享字符串

时间:2017-02-26 11:22:10

标签: multithreading rust

我尝试使用多个std::thread请求多个网址。这是我的代码到目前为止的样子:

fn fetch(urls: Vec<&str>) {
    let (tx, rx) = mpsc::channel();

    for url in urls {
        let tx = tx.clone();

        thread::spawn(|| {
            let ssl = NativeTlsClient::new().unwrap();
            let connector = HttpsConnector::new(ssl);
            let client = Client::with_connector(connector);
            let mut res = client.get(url).send().unwrap();
            let mut result = String::new();
            res.read_to_string(&mut result);

            tx.send(result).unwrap();  
        });
    }

    //let mut result: Vec<String> = vec![];
    for _ in urls {
        println!("{}", rx.recv().unwrap());
    }
}

但是我得到了一个错误:

error[E0277]: the trait bound `std::sync::mpsc::Sender<std::string::String>: std::marker::Sync` is not satisfied
    --> src/lib.rs:18:9
    |
 18 |         thread::spawn(|| {
    |         ^^^^^^^^^^^^^ the trait `std::marker::Sync` is not implemented for `std::sync::mpsc::Sender<std::string::String>`
    |
    = note: `std::sync::mpsc::Sender<std::string::String>` cannot be shared between threads safely
    = note: required because of the requirements on the impl of `std::marker::Send` for `&std::sync::mpsc::Sender<std::string::String>`
    = note: required because it appears within the type `[closure@src/lib.rs:18:23: 29:10 url:&&str, tx:&std::sync::mpsc::Sender<std::string::String>]`
    = note: required by `std::thread::spawn`

当我尝试将move放入thread::spawn

thread::spawn(move || {
    ...

我收到了与生命有关的另一个错误:

error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
  --> src/lib.rs:15:16
   |
15 |     for url in urls {
   |                ^^^^
   |
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the block at 12:26...
  --> src/lib.rs:12:27
   |
12 | fn fetch(urls: Vec<&str>) {
   |                           ^
note: ...so that expression is assignable (expected std::vec::Vec<&str>, found std::vec::Vec<&str>)
  --> src/lib.rs:15:16
   |
15 |     for url in urls {
   |                ^^^^
   = note: but, the lifetime must be valid for the static lifetime...
note: ...so that the type `[closure@src/lib.rs:18:23: 27:10 url:&str, tx:std::sync::mpsc::Sender<std::string::String>]` will meet its required lifetime bounds
  --> src/lib.rs:18:9
   |
18 |         thread::spawn(move || {
   |         ^^^^^^^^^^^^^

那么,通过通道从线程发送字符串的正确方法是什么?如何在以后的错误中解决生命周期问题?

非常感谢你!

1 个答案:

答案 0 :(得分:8)

添加move是您第一个问题的正确解决方案。第二个错误表示代码中存在的问题已经存在,但仅在稍后的编译器阶段检测到。那么第二个错误是什么意思呢?

好吧,生成的线程可以永远运行(更准确地说:只要主线程/整个程序运行)。在你的情况下,他们不会,因为你阻止调用线程等待来自通道的结果。但是编译器并不知道。因此,thread::spawn()要求传递的闭包为: 'static,这意味着它不会引用任何比整个程序短的内容。

但在你的情况下,闭包有一个对url的引用,&str。但该引用背后的字符串实际存在多长时间?不一定永远!这就是问题所在。

这类问题的典型解决方案是使用Arc并将拥有的值包装在其中。但这不可能,因为您的函数无法访问自有值。有几种可能的解决方案:

  • 使用作用域的线程API ,例如crossbeam offers。此API确保生成的线程不会超过其父级,因此您只需引用闭包内的&str即可。我认为这实际上是最好的解决方案,唯一的缺点是引入新的依赖。

  • 将您的功能签名更改为fn fetch(urls: Vec<&'static str>)。它有效,但它限制了函数的调用者,因为它们必须提供静态字符串。我猜这个URL列表不仅仅是一个字符串文字列表,而是动态生成的;所以这对你来说不是一个真正的解决方案。

  • 克隆&str以将结果String移动到闭包中。然而,这不是一个很好的解决方案,因为应该避免使用无用的克隆。但是在你的情况下它可以容忍,因为HTTP请求比克隆一个相当小的(url)字符串要花费更长的时间。