异步rust中的多线程-为什么我的代码无法并行化?

异步rust中的多线程-为什么我的代码无法并行化?,rust,concurrency,rust-actix,Rust,Concurrency,Rust Actix,我试图通过运行以下函数故意耗尽API限制(900次调用): #[get("/exhaust")] pub async fn exhaust(_pool: web::Data<PgPool>, config: web::Data<Arc<Settings>>) -> impl Responder { let mut handles = vec![]; for i in 1..900 { let inne

我试图通过运行以下函数故意耗尽API限制(900次调用):

#[get("/exhaust")]
pub async fn exhaust(_pool: web::Data<PgPool>, config: web::Data<Arc<Settings>>) -> impl Responder {
    let mut handles = vec![];

    for i in 1..900 {
        let inner_config = config.clone();
        let handle = thread::spawn(move || async move {
            println!("running thread {}", i);
            get_single_tweet(inner_config.as_ref().deref(), "1401287393228038149")
                .await
                .unwrap();
        });
        handles.push(handle);
    }

    for h in handles {
        h.join().unwrap().await;
    }

    HttpResponse::Ok()
#[get(“/detain”)]
pub-async-fn-detaxe(_-pool:web::Data,config:web::Data)->impl响应程序{
让mut handles=vec![];
因为我在1..900{
让internal_config=config.clone();
让handle=thread::spawn(移动| |异步移动{
println!(“运行线程{}”,i);
获取单个tweet(内部配置为ref().deref(),“1401287393228038149”)
.等待
.unwrap();
});
把手。推(把手);
}
用于手柄中的h{
h、 连接().展开().等待;
}
HttpResponse::Ok()
我的机器有16个内核,所以我希望上面的运行速度比单线程函数快16倍,但它没有。事实上,它的运行速度和单线程版本一样慢

为什么?我错过了什么


注意:
move | | async move
部分在我看来有点奇怪,但我是根据编译器的建议做到的。由于
async闭包不稳定
,它不允许我将async放在第一个move的旁边。这可能是问题吗?

问题是您以某种方式混合了多线程和异步,从而导致所有的工作都是按顺序进行的:所有线程所做的就是调用
get\u single\u tweet
,这显然是一个
async
函数

现在,在Javascript这样的语言中,
get_single_tweet
将创建一个任务,该任务将返回一个承诺,象征着任务的实现并尽快运行

这不是Rust的工作原理(或者许多其他语言,顺便说一句,Python的行为更像Rust而不是Javascript)。在Rust中,
get\u single\u tweet
只是创造了一个未来,它实际上什么都不做,未来必须是事情发生的时候:

此轮询何时发生?等待的动态链到达事件循环的顶部时

在这里,未来是在线程中创建的,然后从线程返回,然后从
join
获取时等待
-ed,因此您的获取不是在线程中运行,而是在这里运行:

手柄中h的
{
h、 连接().展开().等待;
}
这是完全连续的

现在要解决这个问题,你基本上有两个选择,但重要的是保持你的领域:

  • 如果您想使用线程,那么线程中的调用应该是阻塞的
  • 否则,与其使用您可能想要使用的
    thread::spawn
    (或您选择的运行时中的任何等效项),确保您使用的是多线程运行时可能是一个好主意,但这里我假设
    get_single_tweet
    正在执行IO工作(例如从twitter获取内容),因此单线程运行时可能已经产生了大部分好处

task::spawn
will(如其名称所示)创建一个任务并返回一个句柄,这与Javascript的
async
函数的行为非常接近。

问题在于,您将多线程和异步混合在一起,导致所有工作都是顺序的:您的线程所做的就是调用
get\u single\u tweet
,这显然是一个
async
函数n

现在,在Javascript这样的语言中,
get_single_tweet
将创建一个任务,该任务将返回一个承诺,象征着任务的实现并尽快运行

这不是Rust的工作原理(或者许多其他语言,顺便说一句,Python的行为更像Rust而不是Javascript)。在Rust中,
get\u single\u tweet
只是创造了一个未来,它实际上什么都不做,未来必须是事情发生的时候:

此轮询何时发生?等待的动态链到达事件循环的顶部时

在这里,未来是在线程中创建的,然后从线程返回,然后从
join
获取时等待
-ed,因此您的获取不是在线程中运行,而是在这里运行:

手柄中h的
{
h、 连接().展开().等待;
}
这是完全连续的

现在要解决这个问题,你基本上有两个选择,但重要的是保持你的领域:

  • 如果您想使用线程,那么线程中的调用应该是阻塞的
  • 否则,与其使用您可能想要使用的
    thread::spawn
    (或您选择的运行时中的任何等效项),确保您使用的是多线程运行时可能是一个好主意,但这里我假设
    get_single_tweet
    正在执行IO工作(例如从twitter获取内容),因此单线程运行时可能已经产生了大部分好处

task::spawn
will(如其名称所示)创建一个任务并返回一个句柄,这与Javascript的
异步
函数的行为非常接近。

此代码确实会同步运行
异步
块。
异步
块创建一个实现
未来
的类型,但需要知道的是
未来
不会启动它们必须自己等待,或者交给执行者运行

调用
thread::spawn
时使用一个返回
Future
的闭包将不会执行它们;线程只是创建
async
块并返回。因此
async
块实际上不会执行,直到
句柄
上的循环中等待它们,这将按顺序处理期货

解决此问题的一种方法是使用
futures
板条箱中的
join\u all
同时运行它们

let mut futs = vec![];

for i in 1..900 {
    let inner_config = config.clone();
    futs.push(async move {
        println!("running thread {}", i);
        get_single_tweet(inner_config.as_ref().deref(), "1401287393228038149")
            .await
            .unwrap();
    });
}

futures::future::join_all(futs).await;

此代码确实会运行
async