Actix:将共享数据移动到多个线程的正确方法是什么?

Actix: what's the right way to move shared data into multiple threads?

我有一个 config 结构,我正在我的 actix 应用程序中共享它,如下所示:

pub fn run(addr: &str, pg_pool: PgPool, config: Settings) -> Result<Server, std::io::Error> {
    let pool = web::Data::new(pg_pool); 
    let arc_config = web::Data::new(Arc::new(config)); // <---

    let server = HttpServer::new(move || {
        App::new()
            .service(exhaust)
            .app_data(pool.clone())
            .app_data(arc_config.clone()) // <---
    })
    .bind(addr)?
    .run();

然后我有一个处理程序试图生成多个线程并将该 config 结构传递给每个线程:

#[get("/exhaust")]
pub async fn exhaust(pool: web::Data<PgPool>, config: web::Data<Arc<Settings>>) -> impl Responder {
    for _ in 1..16 {
        let handle = thread::spawn(move || {
            let inner_config = Arc::clone(&config);
            get_single_tweet(inner_config.as_ref().deref(), "1401287393228038149");
        });
    }

    HttpResponse::Ok()
}

我的想法是,因为 config 已经包含在 Arc() 中,所以我应该能够 Arc::clone() 它在每个线程中,然后 deref 到底层变量中。

但是我收到这个错误:

error[E0382]: use of moved value: `config`
  --> src/twitter/routes/pull.rs:63:36
   |
58 | pub async fn exhaust(pool: web::Data<PgPool>, config: web::Data<Arc<Settings>>) -> impl Responder {
   |                                               ------ move occurs because `config` has type `actix_web::web::Data<Arc<Settings>>`, which does not implement the `Copy` trait
...
63 |         let handle = thread::spawn(move || {
   |                                    ^^^^^^^ value moved into closure here, in previous iteration of loop
64 |             let inner_config = Arc::clone(&config);
   |                                            ------ use occurs due to use in closure

我很难理解为什么会失败。如果配置在 Arc 内,那么为什么编译器认为我正在尝试移动它而不是增加引用计数?

我也试过很多其他的方法,都没有成功:

正确的做法是什么?

您需要先克隆它然后它被移动。否则你的第一次迭代将必然需要接受它(因为不能保证 config 仍然存在以在任务运行时被克隆)。然后你会得到你在第二次迭代中看到的错误,因为它也必然需要移动 config;这就是“在之前的循环迭代中,值在此处移入闭包”的意思。

#[get("/exhaust")]
pub async fn exhaust(pool: web::Data<PgPool>, config: web::Data<Arc<Settings>>) -> impl Responder {
    for _ in 1..16 {
        let handle = thread::spawn({
            // Clone this here, so the closure gets its very own to move-capture.
            let inner_config = config.get_ref().clone();
            move || {
                get_single_tweet(inner_config.as_ref().deref(), "1401287393228038149");
            });
        }
    }

    HttpResponse::Ok()
}

请注意 web::Data 本身已经是 just a wrapper around Arc,因此嵌套的 Arc 有一些冗余。你可能只想:

#[get("/exhaust")]
pub async fn exhaust(pool: web::Data<PgPool>, config: web::Data<Settings>) -> impl Responder {
    for _ in 1..16 {
        let handle = thread::spawn({
            let inner_config = config.clone();
            move || {
                get_single_tweet(inner_config.as_ref().deref(), "1401287393228038149");
            });
        }
    }

    HttpResponse::Ok()
}