Skip to content

Commit

Permalink
task: Add more tips + links to spawn_blocking docs
Browse files Browse the repository at this point in the history
I'm working on some code which heavily uses `spawn_blocking`
to run synchronous code, and it took me a while to find and understand
the relevant APIs and patterns here.

First, basically no real world case will spawn
a closure that takes no parameters, so
change the first example to pass an input value, mutate it, and
receive the result.

Let's link to the channel blocking APIs, and provide a small example
of that too.

This also mentions `SyncIoBridge` now.
  • Loading branch information
cgwalters committed Oct 5, 2021
1 parent d047584 commit e2d1642
Showing 1 changed file with 53 additions and 3 deletions.
56 changes: 53 additions & 3 deletions tokio/src/task/blocking.rs
Expand Up @@ -112,27 +112,77 @@ cfg_rt! {
/// still spawn additional threads for blocking operations. The basic
/// scheduler's single thread is only used for asynchronous code.
///
/// # Related APIs and patterns for bridging asynchronous and blocking code
///
/// In simple cases, it is sufficient to have the closure accept input
/// parameters at creation time and return a single value (or struct/tuple, etc.).
///
/// For more complex situations in which it is desirable to stream data to or from
/// the synchronous context, the [`mpsc channel`] has `blocking_send` and
/// `blocking_recv` methods for use in non-async code such as the thread created
/// by `spawn_blocking`.
///
/// Another option is [`SyncIoBridge`] for cases where the synchronous context
/// is operating on byte streams. For example, you might use an asynchronous
/// HTTP client such as [hyper] to fetch data, but perform complex parsing
/// of the payload body using a library written for synchronous I/O.
///
/// [`Builder`]: struct@crate::runtime::Builder
/// [blocking]: ../index.html#cpu-bound-tasks-and-blocking-code
/// [rayon]: https://docs.rs/rayon
/// [`mpsc channel`]: crate::sync::mpsc
/// [`SyncIoBridge`]: https://docs.rs/tokio-util/0.6/tokio_util/io/struct.SyncIoBridge.html
/// [hyper]: https://docs.rs/hyper
/// [`thread::spawn`]: fn@std::thread::spawn
/// [`shutdown_timeout`]: fn@crate::runtime::Runtime::shutdown_timeout
///
/// # Examples
///
/// Pass an input value and receive result of computation:
///
/// ```
/// use tokio::task;
///
/// # async fn docs() -> Result<(), Box<dyn std::error::Error>>{
/// // Initial input
/// let mut v = "Hello, ".to_string();
/// let res = task::spawn_blocking(move || {
/// // do some compute-heavy work or call synchronous code
/// "done computing"
/// // Stand-in for compute-heavy work or using synchronous APIs
/// v.push_str("world");
/// // Pass ownership of the value back to the asynchronous context
/// v
/// }).await?;
///
/// assert_eq!(res, "done computing");
/// // `res` is the value returned from the thread
/// assert_eq!(res.as_str(), "Hello, world");
/// # Ok(())
/// # }
/// ```
///
/// Use a channel:
///
/// ```
/// use tokio::task;
/// use tokio::sync::mpsc;
///
/// # async fn docs() {
/// let (tx, mut rx) = mpsc::channel(2);
/// let start = 5;
/// let worker = task::spawn_blocking(move || {
/// for x in 0..10 {
/// // Stand in for complex computation
/// tx.blocking_send(start + x).unwrap();
/// }
/// });
///
/// let mut acc = 0;
/// while let Some(v) = rx.recv().await {
/// acc += v;
/// }
/// assert_eq!(acc, 95);
/// worker.await.unwrap();
/// # }
/// ```
#[cfg_attr(tokio_track_caller, track_caller)]
pub fn spawn_blocking<F, R>(f: F) -> JoinHandle<R>
where
Expand Down

0 comments on commit e2d1642

Please sign in to comment.