Expand description
The Tokio runtime.
Unlike other Rust programs, asynchronous applications require runtime support. In particular, the following runtime services are necessary:
- An I/O event loop, called the driver, which drives I/O resources and dispatches I/O events to tasks that depend on them.
- A scheduler to execute tasks that use these I/O resources.
- A timer for scheduling work to run after a set period of time.
Tokio’s Runtime
bundles all of these services as a single type, allowing
them to be started, shut down, and configured together. However, most
applications won’t need to use Runtime
directly. Instead, they can
use the tokio::main
attribute macro, which creates a Runtime
under
the hood.
Usage
Most applications will use the tokio::main
attribute macro.
use tokio::net::TcpListener;
use tokio::prelude::*;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut listener = TcpListener::bind("127.0.0.1:8080").await?;
loop {
let (mut socket, _) = listener.accept().await?;
tokio::spawn(async move {
let mut buf = [0; 1024];
// In a loop, read data from the socket and write the data back.
loop {
let n = match socket.read(&mut buf).await {
// socket closed
Ok(n) if n == 0 => return,
Ok(n) => n,
Err(e) => {
println!("failed to read from socket; err = {:?}", e);
return;
}
};
// Write the data back
if let Err(e) = socket.write_all(&buf[0..n]).await {
println!("failed to write to socket; err = {:?}", e);
return;
}
}
});
}
}
From within the context of the runtime, additional tasks are spawned using
the tokio::spawn
function. Futures spawned using this function will be
executed on the same thread pool used by the Runtime
.
A Runtime
instance can also be used directly.
use tokio::net::TcpListener;
use tokio::prelude::*;
use tokio::runtime::Runtime;
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Create the runtime
let mut rt = Runtime::new()?;
// Spawn the root task
rt.block_on(async {
let mut listener = TcpListener::bind("127.0.0.1:8080").await?;
loop {
let (mut socket, _) = listener.accept().await?;
tokio::spawn(async move {
let mut buf = [0; 1024];
// In a loop, read data from the socket and write the data back.
loop {
let n = match socket.read(&mut buf).await {
// socket closed
Ok(n) if n == 0 => return,
Ok(n) => n,
Err(e) => {
println!("failed to read from socket; err = {:?}", e);
return;
}
};
// Write the data back
if let Err(e) = socket.write_all(&buf[0..n]).await {
println!("failed to write to socket; err = {:?}", e);
return;
}
}
});
}
})
}
Runtime Configurations
Tokio provides multiple task scheduling strategies, suitable for different
applications. The runtime builder or #[tokio::main]
attribute may be
used to select which scheduler to use.
Basic Scheduler
The basic scheduler provides a single-threaded future executor. All tasks
will be created and executed on the current thread. The basic scheduler
requires the rt-core
feature flag, and can be selected using the
Builder::basic_scheduler
method:
use tokio::runtime;
let basic_rt = runtime::Builder::new()
.basic_scheduler()
.build()?;
If the rt-core
feature is enabled and rt-threaded
is not,
Runtime::new
will return a basic scheduler runtime by default.
Threaded Scheduler
The threaded scheduler executes futures on a thread pool, using a
work-stealing strategy. By default, it will start a worker thread for each
CPU core available on the system. This tends to be the ideal configurations
for most applications. The threaded scheduler requires the rt-threaded
feature
flag, and can be selected using the Builder::threaded_scheduler
method:
use tokio::runtime;
let threaded_rt = runtime::Builder::new()
.threaded_scheduler()
.build()?;
If the rt-threaded
feature flag is enabled, Runtime::new
will return a
threaded scheduler runtime by default.
Most applications should use the threaded scheduler, except in some niche use-cases, such as when running only a single thread is required.
Resource drivers
When configuring a runtime by hand, no resource drivers are enabled by
default. In this case, attempting to use networking types or time types will
fail. In order to enable these types, the resource drivers must be enabled.
This is done with Builder::enable_io
and Builder::enable_time
. As a
shorthand, Builder::enable_all
enables both resource drivers.
Lifetime of spawned threads
The runtime may spawn threads depending on its configuration and usage. The
threaded scheduler spawns threads to schedule tasks and calls to
spawn_blocking
spawn threads to run blocking operations.
While the Runtime
is active, threads may shutdown after periods of being
idle. Once Runtime
is dropped, all runtime threads are forcibly shutdown.
Any tasks that have not yet completed will be dropped.
Structs
Builds Tokio Runtime with custom configuration values.
Handle to the runtime.
The Tokio runtime.
Error returned by try_current
when no Runtime has been started