A thread pool for running synchronous I/O in asynchronous applications.
In asynchronous code, blocking the thread - that is calling some function which takes a long
time to return - is a very bad idea. It will prevent all the other asynchronous tasks from
running, and can cause all sorts of undesirable behaviour. However, sometimes blocking calls
are needed; for example many libraries are not built with async, but you might want to use
them in an async context. The solution is to have a thread pool where blocking code can be
offloaded to, so that it doesn't block the main asynchronous threads.
In comparison with blocking, another crate that provides similar
functionality, this crate uses a local thread pool instead of a global one. This allows for
multiple thread pools to be created, and each one can be configured, allowing you to fine-tune
your application for maximum speed. Also, this crate has support for spawning blocking
functions that borrow from the outer scope, which is not possible in
blocking.
Call [std::fs::read_to_string] from asynchronous code:
```rust use blocking_pool::ThreadPool;
let pool = ThreadPool::new(); let filename = "file.txt"; let contents = pool.spawnchild(|| std::fs::readto_string(&filename)).await?; println!("The contents of {} is: {}", filename, contents); ```
Thread pools support two methods of running functions: tasks and children,
spawned via spawn_task and spawn_child
respectively. The most important difference is that tasks are required to live for 'static,
whereas children can have any lifetime, allowing them to borrow from the outer scope. The
trade-off is that children cannot be detached to run independently of the outer scope; once you
start one, you must see it to completion straight after.
There are also a few smaller differences between the two:
- Tasks are spawned immediately, whereas children require the returned [Child] to be polled
before it is started.
- [JoinHandle] will catch panics and return an [Err] if your function panicked. [Child]
simply propagates them.
- [JoinHandle] implements both Future and [CompletionFuture],
whereas [Child] only implements [CompletionFuture].
- [JoinHandle] is [Unpin], whereas [Child] is ![Unpin]. This can make [Child]
slightly harder to use.
- [Child] has the type of the function being run as a generic parameter, whereas
[JoinHandle] only has the output type of the function. This makes it difficult to store
[Child] in structs, whereas [JoinHandle] can be stored easily.
- [JoinHandle] has a mandatory heap allocation, whereas [Child] can be theoretically
implemented without any heap allocations at all. Currently it still requires one due to
temporary limitations in Rust.
- Children are slightly faster than tasks due to less synchronization overhead needed.
If you need to detach the function so that it runs in the background, use a task - otherwise, use a child.
License: MIT OR Apache-2.0