2017-02-26 67 views
2

我试图请求多个网址与多个std::thread。这是我的代码看起来是迄今:Rust中的线程之间共享字符串

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 `[[email protected]/lib.rs:18:23: 29:10 url:&&str, tx:&std::sync::mpsc::Sender<std::string::String>]` 
    = note: required by `std::thread::spawn` 

当我试图把movethread::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 `[[email protected]/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 || { 
    |   ^^^^^^^^^^^^^ 

那么,在这里通过通道从线程发送字符串的正确方法是什么?我怎样才能解决后来的错误生活中的问题?

非常感谢!

回答

8

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

那么,一个产生的线程可以永远运行(更确切地说:只要主线程/整个程序运行)。在你的情况下,他们不这样做,因为你阻止了调用线程等待通道的结果。但编译器不知道这一点。因此,thread::spawn()要求传递的封闭为: 'static,这意味着它没有引用任何比整个程序短的任何东西。

但在你的情况下,封闭引用的URL,&str。但该参考背后的字符串实际上存在多久?不一定永远!这是这里的问题。

这样的问题的典型解决方案是使用Arc并包装其中的所有值。但这在这里是不可能的,因为你的函数不能访问所拥有的值。有几个可能的解决方案为您提供:

  • 使用范围的线程API,像crossbeamoffers。这个API确保生成的线程不会超过它的父项,所以你可以在你的闭包中引用&str。我认为这实际上是解决新依赖性的唯一缺点的最佳解决方案。

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

  • 克隆&str将产生的String转换为闭包。但这并不是一个很好的解决方案,因为应该避免使用无用的克隆。但是在你的情况下它可能是可以容忍的,因为HTTP请求比克隆一个相当小的(url)字符串要花费更多的时间。

+1

非常感谢您提供非常详细的答案!第二个解决方案'&'static str'真的对我有用! –