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:
usestd::{net,thread};letlistener=net::TcpListener::bind(address)?;forsocket_resultinlistener.incoming(){letsocket=socket_result?;letgroups=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 ...
Become an O’Reilly member and get unlimited access to this title plus top books and audiobooks from O’Reilly and nearly 200 top publishers, thousands of courses curated by job role, 150+ live events each month,
and much more.
Read now
Unlock full access