【问题标题】:How to initialize thread local variables using async function如何使用异步函数初始化线程局部变量
【发布时间】:2022-11-25 04:39:11
【问题描述】:

我想在程序开始时为所有 4 个线程初始化线程局部变量。

thread_local! {
  static local: i32
}

#[tokio::main(worker_threads = 4)]
async fn main() {
   // local = get_local().await;
}

【问题讨论】:

  • async 当您使用多线程执行器(通过tokio::main)时,任务和线程本地存储通常不能很好地协同工作,其中任务可以在线程之间移动。这里的最终目标是什么?如果您正确使用宏,您的线程局部变量将已经被初始化。
  • 最终目标是将一些 Tonic grpc 客户端放在 thread_local 中,这样我就不需要每次都初始化客户端。但我想还是有可能得到一个通用的解决方案?我这里有固定数量的线程,我的“真正的”程序在初始化完成后启动。

标签: rust rust-tokio


【解决方案1】:

您的 tokio 运行时配置为有 4 个工作线程,您的本地线程提供给主线程,但不提供给工作线程。

如果您打算只初始化一次 gRPC 客户端,OnceCell 可能是合适的:

use once_cell::sync::OnceCell;

pub static CLIENT: OnceCell<hello_world::greeter_client::GreeterClient<tonic::transport::Channel>> =
    OnceCell::new();

pub fn client() -> hello_world::greeter_client::GreeterClient<tonic::transport::Channel> {
    CLIENT.get().unwrap().clone()
}

#[tokio::main]
async fn main() {
    let channel = tonic::transport::Endpoint::new("http://helloworld")
        .unwrap()
        .connect_lazy();
    let client = hello_world::greeter_client::GreeterClient::new(channel);
    CLIENT.set(client).unwrap();
    main_().await;
}

async fn main_() {
    let _ = client()
        .say_hello(hello_world::HelloRequest { name: "foo".into() })
        .await;
}

pub mod hello_world {
    tonic::include_proto!("helloworld");
}

如果你想坚持更类似于本地线程的东西,或者你需要对客户端值有更多的控制,那么你可以使用 tokio 的本地任务。 它允许您为任务提供上下文,但请记住tokio::spawn 引入了新任务,因此当您使用tokio::spawn 时此上下文会丢失。

以下 sn-p 通过 client() 帮助程序函数提供了一个 tonic 客户端,该函数在本地任务上内部调用 .with()。如果未设置本地任务,此函数会发生混乱,如果未提供该值,还有 try_with() 会返回 Result

use tokio::task_local;

task_local! {
    pub static CLIENT: hello_world::greeter_client::GreeterClient<tonic::transport::Channel>
}

pub fn client() -> hello_world::greeter_client::GreeterClient<tonic::transport::Channel> {
    CLIENT.with(|c| c.clone())
}

#[tokio::main]
async fn main() {
    let channel = tonic::transport::Endpoint::new("http://helloworld")
        .unwrap()
        .connect_lazy();
    let client = hello_world::greeter_client::GreeterClient::new(channel);
    CLIENT.scope(client, main_()).await;
}

async fn main_() {
    let _ = client()
        .say_hello(hello_world::HelloRequest { name: "foo".into() })
        .await;
}

pub mod hello_world {
    tonic::include_proto!("helloworld");
}

【讨论】:

    猜你喜欢
    • 2017-05-14
    • 2018-09-23
    • 2019-01-19
    • 2012-08-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-03-17
    • 2016-09-07
    相关资源
    最近更新 更多