亲宝软件园·资讯

展开

C++ Boost Atomic详细讲解

无水先生 人气:0

一、说明

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 是双向内存访问的严格边界。

加载全部内容

相关教程
猜你喜欢
用户评论