第 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?
| Scene | Recommendation |
|---|---|
| Simple counting, auto-increment, auto-decrement | std::atomic |
| a Boolean stop flag | std::atomic<bool> |
| Multiple variables need to be consistent at the same time. | std::mutex |
| Modify the complex state inside containers and objects | std::mutex |
| Need to wait for a certain condition to be met. | std::condition_variable |
Common Errors
- 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. - Using multiple
atomicto represent a single overall state without ensuring consistency between them. - Forcing the use of
atomicon 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.