2017-02-26 10 views
2

複数のURLを複数指定することを試みています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()); 
    } 
} 

しかし、私は言ったエラーました:

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

I:

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` 

私はthread::spawnmoveを入れてみましたときに生涯に関する別のエラーが発生しました:

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を追加することは、最初の問題の正しい解決策です。 2つ目のエラーは、コードの中に既に存在していたが、後のコンパイラ段階でのみ検出された問題を示しています。では、この2番目のエラーはどういう意味ですか?

いいえ、生成されたスレッドは、は、永遠に実行されます(より正確には、メインスレッド/プログラム全体が実行されている限り)。あなたのケースでは、呼び出しスレッドがチャンネルからの結果を待っているのをブロックするので、それらはしません。しかしコンパイラはそれを知らない。したがって、thread::spawn()には、渡されたクロージャが: 'staticであることが必要です。つまり、プログラム全体よりも短いものは参照しません。

あなたのケースでは、クロージャーにURLへの参照があります(&str)。しかし、その参照の後ろの文字列は実際どれくらい長く生きていますか?必ずしも永遠ではありません!それがここの問題です。

これらの問題の典型的な解決方法は、Arcを使用して、その中に所有値をラップすることです。しかし、あなたの関数は所有している値にアクセスできないので、ここではこれはできません。あなたのためのいくつかの可能な解決策があります

  • crossbeamoffersのように、スコープスレッドAPIを使用してください。このAPIは、生成されたスレッドが親よりも長生きしないようにしているため、クロージャー内の&strを参照するだけで済みます。私はこれが実際には新しい依存関係を引き出す唯一の欠点を持つ最良の解決策だと思う。

  • 機能の署名をfn fetch(urls: Vec<&'static str>)に変更してください。それは動作しますが、静的な文字列を提供する必要があるので、関数の呼び出し元を制限します。私は、URLのリストは文字列リテラルのリストではなく、動的に生成されると思います。これは本当にあなたのための解決策ではありません。

  • &strをクローニングして、結果として生じるStringをクロージャ内に移動させる。しかし、無駄なクローンを避けるべきであるので、これは本当に素晴らしい解決策ではありません。しかし、あなたの状況では、HTTP要求がかなり小さい(URL)文字列を複製するよりもずっと長い時間がかかりますので、許容できるかもしれません。

+1

非常に詳細な回答ありがとうございました!2番目のソリューション '& 'static str'は本当に私のために働いた! –

関連する問題