无法在生成的线程中调用函数,因为它"不能满足所需的生命周期"

时间:2015-11-26 12:18:46

标签: multithreading closures rust lifetime

我可以运行此代码

fn testf(host: &str) {}

fn start(host: &str) {
    testf(host);
    testf(host);
}

但出于某种原因,我无法运行这个:

fn testf(host: &str) {}

fn start(host: &str) {
    thread::spawn(move || testf(host));
    thread::spawn(move || testf(host));
}

因为以下错误

src/server.rs:30:5: 30:18 error: the type `[closure@src/server.rs:30:19: 30:38 host:&str]` does not fulfill the required lifetime
src/server.rs:30     thread::spawn(move || testf(host));
                     ^~~~~~~~~~~~~
note: type must outlive the static lifetime
error: aborting due to previous error

有人可以解释一下,它有什么问题以及如何解决它?

2 个答案:

答案 0 :(得分:6)

您的闭包捕获字符串切片,因此其环境的生命周期不会超过此切片的生命周期,但thread::spawn()要求其参数具有静态生命周期:

pub fn spawn<F, T>(f: F) -> JoinHandle<T> 
    where F: FnOnce() -> T, 
          F: Send + 'static, 
          T: Send + 'static

(请注意F: 'static要求)

这是必要的,因为当thread::spawn()生成的线程运行时,可能已经破坏了从中获取切片的字符串。 Rust实际上阻止了代码中的错误!

有几种方法可以解决它。

1)最简单的方法是克隆每个线程的字符串:

fn start(host: &str) {
    {
        let host = host.to_owned();
        thread::spawn(move || testf(&host));
    }
    {
        let host = host.to_owned();
        thread::spawn(move || testf(&host));
    }
}

这样每个线程都会收到自己的字符串副本,当线程本身完成时,该副本将被销毁。

2)如果你知道你的线程应该在start()函数结束之前完成,你可以使用像crossbeam之类的第三方库来将引用传递给生成的线程:

extern crate crossbeam;

fn start(host: &str) {
    crossbeam::scope(|scope| {
        scope.spawn(move || testf(host));
        scope.spawn(move || testf(host));
    });
}

这种方式start()将一直等到scoped()中的两个线程都在返回之前完成,确保任何字符串host指向不会过早被销毁。

以前这样的功能包含在标准库中,但它的实现方式是was found to be unsound,所以它已被弃用;正确替换此功能尚未添加回标准库。

3)甚至另一种选择是使用Arc<String>来共享线程之间的字符串,但这需要在start()之外进行更重要的更改:

use std::sync::Arc;

fn start(host: Arc<String>) {
    {
        let host = host.clone();
        thread::spawn(move || testf(&host));
    }
    {
        let host = host.clone();
        thread::spawn(move || testf(&host));
    }
}

使用这种方法,你需要将你的字符串保持在Arc(这是一个&#34;原子引用计数&#34;指针),所以这要求你改变调用{{1}的代码}。克隆可能更好。当然,如果您想要共享start()而不是&str,其中&SomeStruct很大且/或无法克隆,则无法避免使用xor SomeStruct作为范围。

答案 1 :(得分:0)

线程声明:: spawn函数试图告诉我有关我的问题:)

pub fn spawn<F, T>(f: F) -> JoinHandle<T> where F: FnOnce() -> T, F: Send + 'static, T: Send + 'static

所以,我可以修复使用(host:&amp;&#39; static str)的内容(host:&amp; str)

fn testf(host: &str) {}

fn start(host: &'static str) {
    thread::spawn(move || testf(host));
    thread::spawn(move || testf(host));
}

它对我很有用