Unable to send a &str between threads because it d

2019-07-12 18:39发布

问题:

Given the following simplified program:

#[macro_use] extern crate log;
extern crate ansi_term;
extern crate fern;
extern crate time;
extern crate threadpool;
extern crate id3;

mod logging;

use std::process::{exit, };
use ansi_term::Colour::{Yellow, Green};
use threadpool::ThreadPool;
use std::sync::mpsc::channel;
use std::path::{Path};
use id3::Tag;

fn main() {
    logging::setup_logging();

    let n_jobs = 2;

    let files = vec!(
        "/tmp/The Dynamics - Version Excursions/01-13- Move on Up.mp3",
        "/tmp/The Dynamics - Version Excursions/01-09- Whole Lotta Love.mp3",
        "/tmp/The Dynamics - Version Excursions/01-10- Feel Like Making Love.mp3"
    );
    let pool = ThreadPool::new(n_jobs);
    let (tx, rx) = channel();
    let mut counter = 0;

    for file_ in files {
        let file_ = Path::new(file_);
        counter = counter + 1;
        let tx = tx.clone();

        pool.execute(move || {
            debug!("sending {} from thread", Yellow.paint(counter.to_string()));

            let tag = Tag::read_from_path(file_).unwrap();
            let a_name = tag.artist().unwrap();

            debug!("recursed file from: {} {}",
                   Green.paint(a_name), file_.display());

            tx.send(".").unwrap();
            // TODO amb: not working..
            // tx.send(a_name).unwrap();
        });
    }

    for value in rx.iter().take(counter) {
        debug!("receiving {} from thread", Green.paint(value));
    }
    exit(0);
}

Everything works as expected, unless the one commented line (tx.send(a_name).unwrap();) is put back in. In that case I get the following error:

error: `tag` does not live long enough
            let a_name = tag.artist().unwrap();
                         ^~~
    note: reference must be valid for the static lifetime...
note: ...but borrowed value is only valid for the block suffix following statement 1 at 39:58
            let tag = Tag::read_from_path(file_).unwrap();
            let a_name = tag.artist().unwrap();

            debug!("recursed file from: {} {}",
                   Green.paint(a_name), file_.display());

...

Generally I understand what the compiler tells me, but I don't see a problem since the variable tag is defined inside of the closure block. The only problem that I can guess is, that the variable tx is cloned outside and therefore can collide with the lifetime of tag.

My goal is to put all the current logic in the thread-closure inside of the thread, since this is the "processing" I want to spread to multiple threads. How can I accomplish this, but still send some value to the longer existing tx?

I'm using the following Rust version:

$ rustc --version
rustc 1.9.0 (e4e8b6668 2016-05-18)
$ cargo --version
cargo 0.10.0-nightly (10ddd7d 2016-04-08)

回答1:

a_name is &str borrowed from tag. Its lifetime is therefore bounded by tag. Sending non 'static references down a channel to another thread is unsafe. It refers to something on the threads stack which might not even exist anymore once the receiver tries to access it. In your case you should promote a_name to an owned value of type String, which will be moved to the receiver thread.

tx.send(a_name.to_owned()).unwrap();