如何在Rust的异步函数中使用随机数和tokio::time::sleep?

huangapple go评论75阅读模式
英文:

how can I use a random number and a tokio::time::sleep within an async function in rust

问题

以下是已翻译的内容:

我有一个 gRPC 服务器方法,我想模拟一些工作,为此我有以下代码:

async fn write(&self, request: Request<WriteRequest>)
                 -> Result<Response<WriteResponse>, Status> {
    let mut rng = rand::thread_rng();
    let random = rng.gen_range(1000..5000);
    // Sleep for the random duration
    sleep(Duration::from_millis(random)).await;
    return Ok(Response::new(WriteResponse { ... }));
}

但是代码对于随机值感到不满意,我收到以下错误信息:

未能安全地在线程之间发送 future 由 async 块创建的 future 不是 `Send`

我知道我可以在函数中添加 + Send,但在这种情况下,我无法修改由 gRPC 编译器生成的方法签名。

当我在 Duration 中使用硬编码值时,它可以正常工作。

如何调用随机数生成?或者如何使用 sleep 休眠一个随机值?是否有其他方法?


<details>
<summary>英文:</summary>

I have a gRPC server method for which I want to simulate some work for this I have this

    async fn write(&amp;self, request: Request&lt;WriteRequest&gt;)
                         -&gt; Result&lt;Response&lt;WriteResponse&gt;, Status&gt; {
        let mut rng = rand::thread_rng();
        let random = rng.gen_range(1000..5000);
        // Sleep for the random duration
        sleep(Duration::from_millis(random)).await;
        return Ok(Response::new(WriteResponse { ... }));
    }

but the code is not happy about the random value, I get the error

future cannot be sent between threads safely future created by async block is not Send


I know I can add + Send in the function but in this case, I cannot modify the signature of the method because it is an interface generated by the gRPC compiler

When I used hardcoded value within the Duration in works.

How can I call the random number generation? or how can I sleep for a random value using sleep? is there another way?

</details>


# 答案1
**得分**: 4

这是因为 `rng` 超过了一个等待点而发生的。您可以通过将其封装在一个块中来解决这个问题,这样可以确保它不会存储在异步函数的状态机中。

```rs
let random_secs = {
    let mut rng = rand::thread_rng();
    rng.gen_range(1000..5000)
};

如果您确实需要存储一个 RNG,您可以存储一个由 thread_rng 种子化的普通 PRNG(如 StdRng)。

use rand::{Rng, SeedableRng, rngs::StdRng};
    
let mut rng = {
    let rng = rand::thread_rng();
    StdRng::from_rng(rng).unwrap()
};
    
loop {
    let millis = rng.gen_range(1..10);
    tokio::time::sleep(std::time::Duration::from_millis(millis)).await;
    println!("{millis}");
}
英文:

This happens because rng is held past an await point. You can get around this by enclosing it in a block, which ensures it is not stored in the async function's state machine.

let random_secs = {
    let mut rng = rand::thread_rng();
    rng.gen_range(1000..5000)
};

If you do need to store an RNG, you can store a normal PRNG (such as StdRng) that is seeded from thread_rng.

use rand::{Rng, SeedableRng, rngs::StdRng};
    
let mut rng = {
    let rng = rand::thread_rng();
    StdRng::from_rng(rng).unwrap()
};
    
loop {
    let millis = rng.gen_range(1..10);
    tokio::time::sleep(std::time::Duration::from_millis(millis)).await;
    println!(&quot;{millis}&quot;);
}

huangapple
  • 本文由 发表于 2023年6月8日 00:35:44
  • 转载请务必保留本文链接:https://go.coder-hub.com/76425432.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定