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
内,那么为什么编译器认为我正在尝试移动它而不是增加引用计数?
我也试过很多其他的方法,都没有成功:
- 删除闭包前面的
move
- 编译器抱怨借用的值寿命不够长
- 取消引用
config
并将其包装在一个新的 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()
}
我有一个 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
内,那么为什么编译器认为我正在尝试移动它而不是增加引用计数?
我也试过很多其他的方法,都没有成功:
- 删除闭包前面的
move
- 编译器抱怨借用的值寿命不够长 - 取消引用
config
并将其包装在一个新的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()
}