# How to Use asio io_context_strand for Serialization: A Complete Guide

> Master asio io_context_strand for serialization. Learn the complete guide to ensure handler safety and eliminate concurrent execution. Optimize your async code.

- Repository: [chriskohlhoff/asio](https://github.com/chriskohlhoff/asio)
- Tags: how-to-guide
- Published: 2026-07-17

---

**The `asio::io_context::strand` class guarantees that handlers are never executed concurrently by serializing their execution through an internal queue, eliminating the need for manual synchronization primitives.**

The `asio::io_context::strand` mechanism in the chriskohlhoff/asio repository provides a robust solution for serialization in asynchronous applications. When multiple threads submit work to an `io_context`, strands ensure that handlers execute sequentially without overlapping. This article examines the implementation details from the source code and demonstrates how to leverage strands for guaranteed serialization.

## What is asio::io_context::strand?

`asio::io_context::strand` is a lightweight executor wrapper that ensures **serialized handler execution** on an underlying `io_context`. It leverages the internal `strand_service` to maintain an ordered queue and a "running" flag, preventing concurrent execution of submitted work.

According to the source code in [`include/asio/io_context_strand.hpp`](https://github.com/chriskohlhoff/asio/blob/main/include/asio/io_context_strand.hpp), construction binds the strand to an `io_context` instance (lines 99-104). The strand records handler requests and ensures the next handler does not start until the previous one completes. This architecture eliminates race conditions without requiring explicit mutexes or locks.

## Creating and Using a Strand for Serialization

To create a strand for serialization, instantiate `asio::io_context::strand` with a valid `io_context` reference:

```cpp
asio::io_context io;
asio::io_context::strand strand(io);  // From include/asio/io_context_strand.hpp

```

The strand provides three primary methods for submitting work:

### Posting Handlers

The `post()` method schedules a handler for later execution on the `io_context`:

```cpp
strand.post([]{ 
    std::cout << "Handler executed" << std::endl; 
}, allocator);

```

This guarantees the handler runs only after any previously submitted strand handlers complete.

### Dispatching vs. Deferring

The `dispatch()` method may execute the handler immediately if the strand is idle, while `defer()` always schedules the handler for later execution:

- **`strand.dispatch(handler)`** – Runs immediately if called from within the strand's execution context; otherwise behaves like `post()`.
- **`strand.defer(handler)`** – Always queues the handler, ensuring no immediate execution.

Both methods ultimately invoke the static member functions of `asio::detail::strand_service` (defined in [`include/asio/detail/strand_service.hpp`](https://github.com/chriskohlhoff/asio/blob/main/include/asio/detail/strand_service.hpp)), which maintain the internal queue and serialization state.

### Basic Serialization Example

The following example demonstrates guaranteed serialization using `post()`:

```cpp
#include <asio.hpp>
#include <iostream>
#include <thread>
#include <chrono>

void print_message(const std::string& msg)
{
    std::cout << "Start: " << msg << std::endl;
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
    std::cout << "End:   " << msg << std::endl;
}

int main()
{
    asio::io_context io;
    asio::io_context::strand strand(io);

    // These handlers execute sequentially, never concurrently
    strand.post([]{ print_message("A"); });
    strand.post([]{ print_message("B"); });
    strand.post([]{ print_message("C"); });

    io.run();
}

```

Output shows deterministic ordering without interleaving:

```

Start: A
End:   A
Start: B
End:   B
Start: C
End:   C

```

## Thread Safety and Concurrency Guarantees

The strand mechanism protects against concurrent submissions from multiple threads. Because strands are **lightweight** and copyable, copies share the same underlying state (see the copy constructor in [`include/asio/io_context_strand.hpp`](https://github.com/chriskohlhoff/asio/blob/main/include/asio/io_context_strand.hpp), lines 107-114).

### Multi-Threaded Serialization

Even when threads race to submit work, the strand maintains serialization:

```cpp
std::thread t1([&]{ strand.post([]{ print_message("T1-1"); }); });
std::thread t2([&]{ strand.post([]{ print_message("T2-1"); }); });
t1.join(); 
t2.join();
io.run();  // Executes T1-1 then T2-1 (or vice versa), never interleaved

```

### Checking Execution Context

The `running_in_this_thread()` method returns `true` if the current thread is executing a handler submitted through the strand:

```cpp
if (strand.running_in_this_thread()) {
    // Safe to access strand-protected resources without posting
}

```

## Modern Executor-Based Alternative

For code using the newer executor-based API, the generic `asio::strand` class provides identical serialization guarantees. Defined in [`include/asio/strand.hpp`](https://github.com/chriskohlhoff/asio/blob/main/include/asio/strand.hpp), it works with any executor type:

```cpp
asio::io_context io;
auto exec = asio::make_strand(io.get_executor());
exec.post([]{ print_message("executor-strand"); });
io.run();

```

Both `asio::io_context::strand` and `asio::strand` share the same underlying implementation via `strand_executor_service` (see [`include/asio/detail/strand_executor_service.hpp`](https://github.com/chriskohlhoff/asio/blob/main/include/asio/detail/strand_executor_service.hpp)), ensuring consistent behavior across APIs.

## Key Implementation Files

The serialization mechanism relies on the following source files in the chriskohlhoff/asio repository:

- **[`include/asio/io_context_strand.hpp`](https://github.com/chriskohlhoff/asio/blob/main/include/asio/io_context_strand.hpp)** – Defines `asio::io_context::strand` with constructor at lines 99-104 and copy constructor at lines 107-114.
- **[`include/asio/strand.hpp`](https://github.com/chriskohlhoff/asio/blob/main/include/asio/strand.hpp)** – Provides the generic `asio::strand<Executor>` template and `make_strand()` helper.
- **[`include/asio/detail/strand_service.hpp`](https://github.com/chriskohlhoff/asio/blob/main/include/asio/detail/strand_service.hpp)** – Implements the low-level queue and running flag for the classic strand.
- **[`include/asio/detail/strand_executor_service.hpp`](https://github.com/chriskohlhoff/asio/blob/main/include/asio/detail/strand_executor_service.hpp)** – Underlying service for the generic executor-based strand.

## Summary

- `asio::io_context::strand` guarantees **serialized handler execution** without manual synchronization.
- Use `post()` for deferred execution, `dispatch()` for potential immediate execution, and `defer()` for guaranteed queuing.
- Strands are **copyable and lightweight**, with copies sharing the same serialization state.
- The `running_in_this_thread()` method allows context-aware logic inside strand handlers.
- Modern code should consider `asio::strand` with `make_strand()` for executor compatibility.

## Frequently Asked Questions

### What is the difference between strand.post() and strand.dispatch()?

`strand.post()` always schedules the handler for later execution on the `io_context`, while `strand.dispatch()` may execute the handler immediately if called from within the strand's execution context. If the current thread is not running a strand handler, `dispatch()` behaves identically to `post()`.

### Can I copy an io_context::strand between threads?

Yes. The strand is lightweight and copyable (see lines 107-114 in [`include/asio/io_context_strand.hpp`](https://github.com/chriskohlhoff/asio/blob/main/include/asio/io_context_strand.hpp)), and copies share the same underlying state. This allows passing strands across thread boundaries while maintaining the serialization guarantee for all submitted work.

### How does strand serialization compare to using a mutex?

Strands provide **implicit synchronization** through the `io_context` event loop rather than blocking threads. Unlike mutexes, strands never cause contention or context switches because handlers execute sequentially on the same thread. This model is often more efficient for asynchronous I/O operations than traditional locking.

### Should I use io_context::strand or asio::strand in new code?

For new code using Asio 1.12 or later, prefer `asio::strand` with `asio::make_strand()` as defined in [`include/asio/strand.hpp`](https://github.com/chriskohlhoff/asio/blob/main/include/asio/strand.hpp). This generic executor-based approach integrates better with modern C++ executors and networking TS proposals, while providing identical serialization semantics to the legacy `io_context::strand`.