C++ Boost Lockfree超详细讲解使用方法

 更新时间:2022年11月20日 10:29:40   作者:无水先生  
Boost是为C++语言标准库提供扩展的一些C++程序库的总称。Boost库是一个可移植、提供源代码的C++库,作为标准库的后备,是C++标准化进程的开发引擎之一,是为C++语言标准库提供扩展的一些C++程序库的总称

Boost.Lockfree

一、说明

Boost.Lockfree 提供线程安全和无锁容器。可以从多个线程访问此库中的容器,而无需同步访问。

在 1.56.0 版本中,Boost.Lockfree 只提供了两个容器:boost::lockfree::queue 类型的队列和 boost::lockfree::stack 类型的栈。对于队列,可以使用第二个实现:boost::lockfree::spsc_queue。此类针对只有一个线程写入队列和只有一个线程从队列读取的用例进行了优化。类名中的缩写 spsc 代表单一生产者/单一消费者。

二、示例和代码

示例 46.1。使用 boost::lockfree::spsc_queue

#include <boost/lockfree/spsc_queue.hpp>
#include <thread>
#include <iostream>
boost::lockfree::spsc_queue<int> q{100};
int sum = 0;
void produce()
{
  for (int i = 1; i <= 100; ++i)
    q.push(i);
}
void consume()
{
  int i;
  while (q.pop(i))
    sum += i;
}
int main()
{
  std::thread t1{produce};
  std::thread t2{consume};
  t1.join();
  t2.join();
  consume();
  std::cout << sum << '\n';
}

Example46.1

示例 46.1 使用容器 boost::lockfree::spsc_queue。第一个执行函数 produce() 的线程将数字 1 到 100 添加到容器中。第二个线程执行 consume(),从容器中读取数字并将它们相加。因为容器 boost::lockfree::spsc_queue 明确支持来自两个线程的并发访问,所以不需要同步线程。

请注意,函数 consume() 会在线程终止后被第二次调用。这需要计算所有 100 个数字的总数,即 5050。因为 consume() 在循环中访问队列,它读取数字的速度可能比 produce() 插入数字的速度快。如果队列为空,pop() 返回 false。因此,执行 consume() 的线程可能会终止,因为另一个线程中的 produce() 无法足够快地填充队列。如果执行 produce() 的线程终止,那么很明显所有数字都已添加到队列中。第二次调用 consume() 确保将可能尚未读取的数字添加到 sum 中。

队列的大小被传递给构造函数。因为 boost::lockfree::spsc_queue 是用循环缓冲区实现的,所以示例 46.1 中的队列容量为 100 个元素。如果由于队列已满而无法添加值,则 push() 返回 false。该示例不检查 push() 的返回值,因为恰好有 100 个数字被添加到队列中。因此,100 个元素就足够了。

示例 46.2。 boost::lockfree::spsc_queue 和 boost::lockfree::capacity

#include <boost/lockfree/spsc_queue.hpp>
#include <boost/lockfree/policies.hpp>
#include <thread>
#include <iostream>
using namespace boost::lockfree;
spsc_queue<int, capacity<100>> q;
int sum = 0;
void produce()
{
  for (int i = 1; i <= 100; ++i)
    q.push(i);
}
void consume()
{
  while (q.consume_one([](int i){ sum += i; }))
    ;
}
int main()
{
  std::thread t1{produce};
  std::thread t2{consume};
  t1.join();
  t2.join();
  q.consume_all([](int i){ sum += i; });
  std::cout << sum << '\n';
}

Example46.2

示例 46.2 与前面的示例类似,但这次循环缓冲区的大小是在编译时设置的。这是通过模板 boost::lockfree::capacity 完成的,它需要容量作为模板参数。 q 是用默认构造函数实例化的——容量不能在运行时设置。

函数 consume() 已更改为使用 consume_one() 而不是 pop() 来读取数字。 lambda 函数作为参数传递给 consume_one()。 consume_one() 就像 pop() 一样读取一个数字,但该数字不是通过对调用者的引用返回的。它作为唯一参数传递给 lambda 函数。

当线程终止时,main() 调用成员函数 consume_all(),而不是 consume()。 consume_all() 的工作方式与 consume_one() 类似,但要确保队列在调用后为空。只要队列中有元素,consume_all() 就会调用 lambda 函数。

示例 46.2 再次将 5050 写入标准输出。

示例 46.3。具有可变容器大小的 boost::lockfree::queue

#include <boost/lockfree/queue.hpp>
#include <thread>
#include <atomic>
#include <iostream>
boost::lockfree::queue<int> q{100};
std::atomic<int> sum{0};
void produce()
{
  for (int i = 1; i <= 10000; ++i)
    q.push(i);
}
void consume()
{
  int i;
  while (q.pop(i))
    sum += i;
}
int main()
{
  std::thread t1{produce};
  std::thread t2{consume};
  std::thread t3{consume};
  t1.join();
  t2.join();
  t3.join();
  consume();
  std::cout << sum << '\n';
}

Example46.3

示例 46.3 在两个线程中执行 consume()。因为有多个线程从队列中读取,所以不得使用类 boost::lockfree::spsc_queue。此示例改为使用 boost::lockfree::queue。

多亏了 std::atomic,对变量 sum 的访问现在也是线程安全的。

队列的大小设置为 100——这是传递给构造函数的参数。但是,这只是初始大小。默认情况下,boost::lockfree::queue 不使用循环缓冲区实现。如果添加到队列中的项目多于设置的容量,则会自动增加。如果初始大小不够,boost::lockfree::queue 会动态分配额外的内存。

这意味着 boost::lockfree::queue 不一定是无锁的。 boost::lockfree::queue 默认使用的分配器是 boost::lockfree::allocator,它基于 std::allocator。因此,此分配器确定 boost::lockfree::queue 是否是无约束的无锁。

#include <boost/lockfree/queue.hpp>
#include <thread>
#include <atomic>
#include <iostream>
using namespace boost::lockfree;
queue<int, fixed_sized<true>> q{10000};
std::atomic<int> sum{0};
void produce()
{
  for (int i = 1; i <= 10000; ++i)
    q.push(i);
}
void consume()
{
  int i;
  while (q.pop(i))
    sum += i;
}
int main()
{
  std::thread t1{produce};
  std::thread t2{consume};
  std::thread t3{consume};
  t1.join();
  t2.join();
  t3.join();
  consume();
  std::cout << sum << '\n';
}

Example46.4

示例 46.3 在两个线程中执行 consume()。因为有多个线程从队列中读取,所以不得使用类 boost::lockfree::spsc_queue。此示例改为使用 boost::lockfree::queue。

多亏了 std::atomic,对变量 sum 的访问现在也是线程安全的。

队列的大小设置为 100——这是传递给构造函数的参数。但是,这只是初始大小。默认情况下,boost::lockfree::queue 不使用循环缓冲区实现。如果添加到队列中的项目多于设置的容量,则会自动增加。如果初始大小不够,boost::lockfree::queue 会动态分配额外的内存。

这意味着 boost::lockfree::queue 不一定是无锁的。 boost::lockfree::queue 默认使用的分配器是 boost::lockfree::allocator,它基于 std::allocator。因此,此分配器确定 boost::lockfree::queue 是否是无约束的无锁。

示例 46.4。具有恒定容器大小的 boost::lockfree::queue

到此这篇关于C++ Boost Lockfree超详细讲解使用方法的文章就介绍到这了,更多相关C++ Boost Lockfree内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • VS2019添加引用出错:对COM组件的调用返回了错误HRESULT E_FAIL(未能完成操作未指定的错误)

    VS2019添加引用出错:对COM组件的调用返回了错误HRESULT E_FAIL(未能完成操作未指定的错误)

    这篇文章主要介绍了VS2019添加引用出错:对COM组件的调用返回了错误HRESULT E_FAIL(未能完成操作。未指定的错误),需要的朋友可以参考下
    2020-07-07
  • C语言详细分析宏定义的使用

    C语言详细分析宏定义的使用

    宏定义是用宏名来表示一个字符串,在宏展开时又以该字符串取代宏名,这只是一种简单的替换。字符串中可以含任何字符,可以是常数,也可以是表达式,预处理程序对它不作任何检查,如有错误,只能在编译已被宏展开后的源程序时发现
    2022-04-04
  • C++实现五子棋游戏

    C++实现五子棋游戏

    这篇文章主要为大家详细介绍了C++实现五子棋游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-01-01
  • C++ cin不同状态详细讲解

    C++ cin不同状态详细讲解

    cin是C++编程语言中的标准输入流对象,即istream类的对象。cin主要用于从标准输入读取数据,这里的标准输入,指的是终端的键盘。此外,cout是流的对象,即ostream类的对象,cerr是标准错误输出流的对象,也是ostream类的对象
    2022-10-10
  • C语言实现电子秒表

    C语言实现电子秒表

    这篇文章主要为大家详细介绍了C语言实现电子秒表,毫秒级秒表,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-06-06
  • C语言数组超详细讲解中篇三子棋

    C语言数组超详细讲解中篇三子棋

    数组是一组有序的数据的集合,本篇将带你结合数组来实现三子棋小游戏,上手实练更快的能够掌握数组使用,感兴趣的朋友来看看吧
    2022-04-04
  • Qt在vs2019中使用及设置方法

    Qt在vs2019中使用及设置方法

    这篇文章主要介绍了Qt在vs2019中使用及设置方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-08-08
  • 详解C语言中rand函数的使用

    详解C语言中rand函数的使用

    在编程时我们有时总希望自己产生一个随机数字,以供使用,那么下面介绍rand函数的使用,有需要的可以参考学习。
    2016-08-08
  • 基于c++强制类型转换的(总结)详解

    基于c++强制类型转换的(总结)详解

    本篇文章对C++中的强制类型转换进行了详细的分析介绍。需要的朋友参考下
    2013-05-05
  • 深入了解C语言中常见的文件操作方法

    深入了解C语言中常见的文件操作方法

    这篇文章主要为大家详细介绍了C语言中常见的文件操作,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-06-06

最新评论