Chapter 20. Asynchronous Programming
Suppose youâre writing a chat server. For each network connection, there are incoming packets to parse, outgoing packets to assemble, security parameters to manage, chat group subscriptions to track, and so on. Managing all this for many connections simultaneously is going to take some organization.
Ideally, you could just start a separate thread for each incoming connection:
use
std
::{
net
,
thread
};
let
listener
=
net
::TcpListener
::bind
(
address
)
?
;
for
socket_result
in
listener
.
incoming
()
{
let
socket
=
socket_result
?
;
let
groups
=
chat_group_table
.
clone
();
thread
::spawn
(
||
{
log_error
(
serve
(
socket
,
groups
));
});
}
For each new connection, this spawns a fresh thread running the serve
function, which is able to focus on managing a single connectionâs needs.
This works well, until everything goes much better than planned and suddenly you have tens of thousands of users. Itâs not unusual for a threadâs stack to grow to 100 KiB or more, and that is probably not how you want to spend gigabytes of server memory. Threads are good and necessary for distributing work across multiple processors, but their memory demands are such that we often need complementary ways, used together with threads, to break the work down.
You can use Rust asynchronous tasks to interleave many independent activities on a single thread or a pool of worker threads. Asynchronous tasks are similar to threads, but are much quicker to create, pass control amongst themselves ...
Get Programming Rust, 2nd Edition now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.