一、说明
Boost.Atomic 提供类 boost::atomic,可用于创建原子变量。它们被称为原子变量,因为所有访问都是原子的。 Boost.Atomic 用于多线程程序,当在一个线程中访问变量不应被访问相同变量的另一个线程中断时。如果没有 boost::atomic,从多个线程访问共享变量的尝试将需要与锁同步。
boost::atomic 取决于支持原子变量访问的目标平台。否则,boost::atomic 使用锁。该库允许您检测目标平台是否支持原子变量访问。
如果您的开发环境支持 C++11,则不需要 Boost.Atomic。 C++11 标准库提供了一个头文件 atomic,它定义了与 Boost.Atomic 相同的功能。例如,您会发现一个名为 std::atomic 的类。
Boost.Atomic 支持与标准库大致相同的功能。虽然一些函数在 Boost.Atomic 中被重载,但它们在标准库中可能有不同的名称。标准库还提供了一些 Boost.Atomic 中缺少的函数,例如 std::atomic_init() 和 std::kill_dependency()。
二、示例和代码
示例 45.1。使用 boost::atomic
Boost.Atomic
#include <boost/atomic.hpp>
#include <thread>
#include <iostream>
boost::atomic<int> a{ 0 };
void thread()
{
++a;
}
void thread_s()
{
std::cout << "Hello Thread" << a << '\n';
}
int main()
{
std::thread t1{ thread };
std::thread t2{ thread };
std::thread t3{ thread_s };
std::thread t4{ thread_s };
t1.join();
t2.join();
t3.join();
t4.join();
std::cout << a << '\n';
}
Example45.1
示例 45.1 使用两个线程来递增 int 变量 a。该示例使用 boost::atomic 代替锁来对 a 进行原子访问。该示例将 2 写入标准输出。
boost::atomic 之所以有效,是因为一些处理器支持对变量的原子访问。如果增加一个 int 变量是一个原子操作,则不需要锁。如果此示例在无法将变量递增为原子操作的平台上运行,则 boost::atomic 使用锁。
示例 45.2。 boost::atomic 带锁或不带锁
#include <boost/atomic.hpp>
#include <iostream>
int main()
{
std::cout.setf(std::ios::boolalpha);
boost::atomic<short> s;
std::cout << s.is_lock_free() << '\n';
boost::atomic<int> i;
std::cout << i.is_lock_free() << '\n';
boost::atomic<long> l;
std::cout << l.is_lock_free() << '\n';
}
您可以在原子变量上调用 is_lock_free() 来检查是否在没有锁的情况下访问该变量。如果您在 Intel x86 处理器上运行示例,它会显示 true 三次。如果您在没有对 short、int 和 long 变量进行无锁访问的处理器上运行它,则会显示 false。
Boost.Atomic 提供了 BOOST_ATOMIC_INT_LOCK_FREE 和 BOOST_ATOMIC_LONG_LOCK_FREE 宏来在编译时检查哪些数据类型支持无锁访问。
示例 45.2 仅使用整型数据类型。您不应将 boost::atomic 与 std::string 或 std::vector 等类一起使用。 Boost.Atomic 支持整数、指针、布尔值 (bool) 和普通类。整数类型的示例包括 short、int 和 long。普通类定义可以使用 std::memcpy() 复制的对象。
示例 45.3。 boost::atomic 和 boost::memory_order_seq_cst
#include <boost/atomic.hpp>
#include <thread>
#include <iostream>
boost::atomic<int> a{0};
void thread()
{
a.fetch_add(1, boost::memory_order_seq_cst);
}
int main()
{
std::thread t1{thread};
std::thread t2{thread};
t1.join();
t2.join();
std::cout << a << '\n';
}
Example45.3E
示例 45.3 增加了两次——这次不是使用 operator++,而是调用 fetch_add()。成员函数 fetch_add() 可以采用两个参数:a 应该递增的数字和内存顺序。
内存顺序指定内存访问操作必须发生的顺序。默认情况下,这个顺序是不确定的,不依赖于代码行的顺序。只要程序表现得好像内存访问操作是按源代码顺序执行的,编译器和处理器就可以更改顺序。此规则仅适用于线程。如果使用多个线程,内存访问顺序的变化会导致程序运行错误。 Boost.Atomic 支持在访问变量时指定内存顺序,以确保内存访问在多线程程序中以所需的顺序发生。
注意
指定内存顺序可优化性能,但会增加复杂性并使编写正确代码变得更加困难。因此,在实践中,您应该有充分的理由使用内存顺序。
示例 45.3 使用内存顺序 boost::memory_order_seq_cst 将 a 递增 1。内存顺序代表顺序一致性。这是最严格的内存顺序。在 fetch_add() 调用之前出现的所有内存访问必须在执行此成员函数之前发生。在 fetch_add() 调用之后出现的所有内存访问都必须在执行此成员函数之后发生。编译器和处理器可以在调用 fetch_add() 之前和之后重新排序内存访问,但它们不得将内存访问从调用 fetch_add() 之前移动到调用之后,反之亦然。 boost::memory_order_seq_cst 是双向内存访问的严格边界。
到此这篇关于C++ Boost Atomic详细讲解的文章就介绍到这了,更多相关C++ Boost Atomic内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!