第 18.19.3 節

std::atomic

0瀏覽次數0訪問次數--跳出率--平均停留

What problem does this section solve?

If the shared data is just a simple counter, you can use std::atomic<int>. It ensures that operations like ++counter are thread-safe, without the need for manual locking.

atomic is suitable for simple independent atomic operations. When multiple variables need to be consistent, or when complex logic needs to be executed, mutex should still be used.

Example code

Example 1: Thread-safe counter using atomic

#include <atomic>
#include <iostream>
#include <thread>
#include <vector>

// atomic 提供原子操作,适合简单的跨线程共享状态。
std::atomic<int> counter{0};

void add_many(int times)
{
    for (int i = 0; i < times; ++i)
    {
        ++counter;
    }
}

int main()
{
    // 程序从 main 函数开始执行,下面的语句会按顺序运行。
    // vector 是动态数组,元素数量可以在运行时变化。
    std::vector<std::thread> threads;

    for (int i = 0; i < 4; ++i)
    {
        threads.emplace_back(add_many, 100000);
    }

    for (auto& thread : threads)
    {
        // join 会等待子线程结束,避免 main 提前退出。
        thread.join();
    }

    std::cout << "counter = " << counter << "\n";
    std::cout << "expected = 400000\n";

    return 0;
}

Results

counter = 400000
expected = 400000

Example 2: atomic is suitable for state marking.

Here is an example using an atomic boolean as a stop flag. The main thread modifies the flag, and the worker thread can safely see the change.

#include <atomic>
#include <chrono>
#include <iostream>
#include <thread>

// atomic 提供原子操作,适合简单的跨线程共享状态。
std::atomic<bool> running{true};

void worker()
{
    int count = 0;
    while (running)
    {
        ++count;
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
    }

    std::cout << "worker count = " << count << "\n";
}

int main()
{
    // 程序从 main 函数开始执行,下面的语句会按顺序运行。
    // 创建子线程,让这部分代码和 main 线程并发运行。
    std::thread t(worker);

    std::this_thread::sleep_for(std::chrono::milliseconds(350));
    running = false;

    // join 会等待子线程结束,避免 main 提前退出。
    t.join();
    std::cout << "stopped\n";

    return 0;
}

One possible outcome

worker count = 4
stopped

How to choose between mutex and atomic?

SceneRecommendation
Simple counting, auto-increment, auto-decrementstd::atomic
a Boolean stop flagstd::atomic<bool>
Multiple variables need to be consistent at the same time.std::mutex
Modify the complex state inside containers and objectsstd::mutex
Need to wait for a certain condition to be met.std::condition_variable

Common Errors

  1. Just because you use atomic, doesn't mean the entire object becomes thread-safe. Only the operations on that atomic variable itself are thread-safe.
  2. Using multiple atomic to represent a single overall state without ensuring consistency between them.
  3. Forcing the use of atomic on complex shared data structures makes the code both hard to understand and error-prone.

Summary

  • std::atomic<T> Ensures thread safety for basic operations on simple variables.
  • Simple counters and stop markers are very suitable for microcontrollers.
  • Complex shared states should still use mutex.
  • In the initial stage, there's no need to delve into memory ordering—just master the default usage and understand its applicable boundaries.
音乐页