Search code examples
rustasync-awaitrust-tokio

When should you use Tokio's `spawn_blocking`?


In the task documentation, there's a section that talks about calling blocking code in async, and how that should be avoided as to not block the async thread too much (https://docs.rs/tokio/1.21.2/tokio/task/index.html#blocking-and-yielding).

It also talks about using tokio::task::spawn_blocking for these tasks, but I'm wondering at which point sending the work to a different thread is recommended? I'm currently writing a program that recovers a large amount of ecdsa signatures, which takes about 100 microseconds per message, all while doing a large amount of network IO. As a concrete example, is this enough to use things like spawn_blocking?


Solution

  • Alice Rhyl (one of the Tokio devs) has a good blog post on blocking in async code.

    One of the key parts for your question:

    To give a sense of scale of how much time is too much, a good rule of thumb is no more than 10 to 100 microseconds between each .await. That said, this depends on the kind of application you are writing.

    Given that you're spending 100 microseconds per message, I think you're likely to be getting to the point where moving to a different thread is the right call.

    The post also gives a rule of thumb for how to approach moving the work onto different threads:

    • For synchronous IO, use spawn_blocking.
    • For CPU-bound computation, use a separate fork-join thread pool like rayon.
    • For synchronous work that runs forever (e.g. listening to a database connection), spawn your own dedicated thread to avoid taking one away from the Tokio/Rayon pool.