Module sync
Synchronization primitives for use in asynchronous contexts.
Tokio programs tend to be organized as a set of tasks where each task operates independently and may be executed on separate physical threads. The synchronization primitives provided in this module permit these independent tasks to communicate together.
Message passing
The most common form of synchronization in a Tokio program is message passing. Two tasks operate independently and send messages to each other to synchronize. Doing so has the advantage of avoiding shared state.
Message passing is implemented using channels. A channel supports sending a message from one producer task to one or more consumer tasks. There are a few flavors of channels provided by Tokio. Each channel flavor supports different message passing patterns. When a channel supports multiple producers, many separate tasks may send messages. When a channel supports multiple consumers, many different separate tasks may receive messages.
Tokio provides many different channel flavors as different message passing patterns are best handled with different implementations.
oneshot channel
The [oneshot channel][oneshot] supports sending a single value from a
single producer to a single consumer. This channel is usually used to send
the result of a computation to a waiter.
Example: using a [oneshot channel][oneshot] to receive the result of a
computation.
use oneshot;
async
async
Note, if the task produces a computation result as its final
action before terminating, the JoinHandle can be used to
receive that value instead of allocating resources for the
oneshot channel. Awaiting on JoinHandle returns Result. If
the task panics, the Joinhandle yields Err with the panic
cause.
Example:
async
async
mpsc channel
The [mpsc channel][mpsc] supports sending many values from many
producers to a single consumer. This channel is often used to send work to a
task or to receive the result of many computations.
This is also the channel you should use if you want to send many messages from a single producer to a single consumer. There is no dedicated spsc channel.
Example: using an mpsc to incrementally stream the results of a series of computations.
use mpsc;
async
async
The argument to mpsc::channel is the channel capacity. This is the maximum
number of values that can be stored in the channel pending receipt at any
given time. Properly setting this value is key in implementing robust
programs as the channel capacity plays a critical part in handling back
pressure.
A common concurrency pattern for resource management is to spawn a task dedicated to managing that resource and using message passing between other tasks to interact with the resource. The resource may be anything that may not be concurrently used. Some examples include a socket and program state. For example, if multiple tasks need to send data over a single socket, spawn a task to manage the socket and use a channel to synchronize.
Example: sending data from many tasks over a single socket using message passing.
use tokio::io::{self, AsyncWriteExt};
use tokio::net::TcpStream;
use tokio::sync::mpsc;
#[tokio::main]
async fn main() -> io::Result<()> {
let mut socket = TcpStream::connect("www.example.com:1234").await?;
let (tx, mut rx) = mpsc::channel(100);
for _ in 0..10 {
// Each task needs its own `tx` handle. This is done by cloning the
// original handle.
let tx = tx.clone();
tokio::spawn(async move {
tx.send(&b"data to write"[..]).await.unwrap();
});
}
// The `rx` half of the channel returns `None` once **all** `tx` clones
// drop. To ensure `None` is returned, drop the handle owned by the
// current task. If this `tx` handle is not dropped, there will always
// be a single outstanding `tx` handle.
drop(tx);
while let Some(res) = rx.recv().await {
socket.write_all(res).await?;
}
Ok(())
}
The mpsc and oneshot channels can be combined to provide a request /
response type synchronization pattern with a shared resource. A task is
spawned to synchronize a resource and waits on commands received on a
mpsc channel. Each command includes a oneshot Sender on which the
result of the command is sent.
Example: use a task to synchronize a u64 counter. Each task sends an
"fetch and increment" command. The counter value before the increment is
sent over the provided oneshot channel.
use ;
use Increment;
async
broadcast channel
The broadcast channel supports sending many values from
many producers to many consumers. Each consumer will receive
each value. This channel can be used to implement "fan out" style
patterns common with pub / sub or "chat" systems.
This channel tends to be used less often than oneshot and mpsc but still
has its use cases.
This is also the channel you should use if you want to broadcast values from a single producer to many consumers. There is no dedicated spmc broadcast channel.
Basic usage
use broadcast;
async
watch channel
The watch channel supports sending many values from a many
producer to many consumers. However, only the most recent value is
stored in the channel. Consumers are notified when a new value is sent, but
there is no guarantee that consumers will see all values.
The watch channel is similar to a broadcast channel with capacity 1.
Use cases for the watch channel include broadcasting configuration
changes or signalling program state changes, such as transitioning to
shutdown.
Example: use a watch channel to notify tasks of configuration
changes. In this example, a configuration file is checked periodically. When
the file changes, the configuration changes are signalled to consumers.
use watch;
use ;
use io;
async
async
State synchronization
The remaining synchronization primitives focus on synchronizing state.
These are asynchronous equivalents to versions provided by std. They
operate in a similar way as their std counterparts but will wait
asynchronously instead of blocking the thread.
-
BarrierEnsures multiple tasks will wait for each other to reach a point in the program, before continuing execution all together. -
MutexMutual Exclusion mechanism, which ensures that at most one thread at a time is able to access some data. -
NotifyBasic task notification.Notifysupports notifying a receiving task without sending data. In this case, the task wakes up and resumes processing. -
RwLockProvides a mutual exclusion mechanism which allows multiple readers at the same time, while allowing only one writer at a time. In some cases, this can be more efficient than a mutex. -
SemaphoreLimits the amount of concurrency. A semaphore holds a number of permits, which tasks may request in order to enter a critical section. Semaphores are useful for implementing limiting or bounding of any kind.
Runtime compatibility
All synchronization primitives provided in this module are runtime agnostic. You can freely move them between different instances of the Tokio runtime or even use them from non-Tokio runtimes.
When used in a Tokio runtime, the synchronization primitives participate in cooperative scheduling to avoid starvation. This feature does not apply when used from non-Tokio runtimes.
As an exception, methods ending in _timeout are not runtime agnostic
because they require access to the Tokio timer. See the documentation of
each *_timeout method for more information on its use.
Modules
- broadcast A multi-producer, multi-consumer broadcast queue. Each sent value is seen by all consumers.
- futures Named future types.
- mpsc A multi-producer, single-consumer queue for sending values between asynchronous tasks.
-
oneshot
A one-shot channel is used for sending a single message between
asynchronous tasks. The
channelfunction is used to create aSenderandReceiverhandle pair that form the channel.[
error::RecvError]: -
watch
A multi-producer, multi-consumer channel that only retains the last sent
value.
[
Receiver::borrow_and_update()]: [channel]: crate::sync::watch::channel [Sender::is_closed]: crate::sync::watch::Sender::is_closed [Sender::closed]: crate::sync::watch::Sender::closed [Sender::subscribe()]: crate::sync::watch::Sender::subscribe