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

目录
  • 一、说明
  • 二、示例和代码

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内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

时间: 2022-11-19

C++&nbsp;boost&nbsp;scoped_ptr智能指针详解

目录 一.智能指针-唯一所有者 二.接口类分析 一.智能指针-唯一所有者 boost::scoped_ptr 是一个智能指针,它是动态分配对象的唯一所有者. boost::scoped_ptr 无法复制或移动.此智能指针在头文件 boost/scoped_ptr.hpp 中定义. 二.接口类分析 scoped_array 分析 scoped_array 的类部分原始代码如下: template<class T> class scoped_array // noncopyable { priva

C++&nbsp;Boost&nbsp;PointerContainer智能指针详解

目录 一.提要 二.智能指针Boost.PointerContainer 三.练习 一.提要 在 C++11 中,Boost.PointerContainer是另一个智能指针,一般是用来生成集合数据的,本文阐述这种指针的特点和用法. 二.智能指针Boost.PointerContainer 库 Boost.PointerContainer 提供专门用于管理动态分配对象的容器.例如,在 C++11 中,您可以使用 std::vector<std::unique_ptr<int>> 创

C++ Boost weak_ptr智能指针超详细讲解

目录 一.提要 二.特别智能指针(Special Smart Pointers) 一.提要 在 C++11 中,boost::weak_ptr是另一类智能指针,一般是用COM组件生成.调用,本文阐述这种指针的特点和用法. 二.特别智能指针(Special Smart Pointers) 到目前为止介绍的每个智能指针都可以在不同的场景中单独使用.但是,boost::weak_ptr 仅在与 boost::shared_ptr 结合使用时才有意义. boost::weak_ptr 在 boost/w

C++&nbsp;Boost&nbsp;shared_ptr共享指针详细讲解

目录 一.提要 二.智能指针boost::shared_ptr与boost::scoped_ptr 三.智能指针 boost::shared_ptr用法 示例1 示例2 示例3 示例4 示例5 一.提要 boost::shared_ptr是另一个智能指针,与 boost::scoped_ptr有很大不同,本文阐述这种区别. 二.智能指针boost::shared_ptr与boost::scoped_ptr 主要区别在于: boost::shared_ptr 不一定是对象的独占所有者. 所有权可以

C++&nbsp;Boost&nbsp;Lambda表达式详解

目录 lambda表达式格式 说明Boost.Lambda lambda表达式格式 lambda表达式的格式 [捕捉列表](参数列表)mutable->返回值类型{ 语句部分 }; 其中参数列表.返回值类型是可选的,捕捉列表.函数体可以为空. 先来看一个较为简单的lamda表达式 int main(void) { auto add = [](int a, int b)->int {return a + b; }; cout << add(1, 2) << endl; r

C++&nbsp;Boost&nbsp;MPI接口详细讲解

目录 一.说明 二.开发和运行时环境 三.简单数据交换 一.说明 Boost.MPI 提供了 MPI 标准(消息传递接口)的接口.该标准简化了并发执行任务的程序的开发.您可以使用线程或通过共享内存或网络连接使多个进程相互通信来开发此类程序. MPI 的优点是你不需要关心这些细节.您可以完全专注于并行化您的程序. 缺点是您需要 MPI 运行时环境.如果您控制运行时环境,MPI 只是一个选项.例如,如果你想分发一个可以通过双击启动的程序,你将无法使用 MPI.虽然操作系统开箱即用地支持线程.共享内存

C++&nbsp;Boost&nbsp;Bimap示例详细讲解

目录 一.提要 二.示例 练习 一.提要 库 Boost.Bimap 基于 Boost.MultiIndex 并提供了一个无需先定义即可立即使用的容器.该容器类似于 std::map,但支持从任一侧查找值. Boost.Bimap 允许您根据访问地图的方式创建任意一侧都可以作为关键点的地图.当您访问左侧作为键时,右侧是值,反之亦然. 二.示例 Example13.1.Usingboost::bimap #include <boost/bimap.hpp> #include <string

C++&nbsp;Boost&nbsp;Tokenizer使用详细讲解

目录 介绍 示例一 示例二 示例三 示例四 示例五 示例六 示例七 介绍 库 Boost.Tokenizer 允许您通过将某些字符解释为分隔符来迭代字符串中的部分表达式.使用 boost::tokenizer 迭代字符串中的部分表达式 示例一 使用 boost::tokenizer 迭代字符串中的部分表达式 #include <boost/tokenizer.hpp> #include <string> #include <iostream> int main() {

MySQL/Postgrsql 详细讲解如何用ODBC接口访问MySQL指南

详细讲解如何用ODBC接口访问MySQL指南 MySQL的ODBC接口实现是通过安装MyODBC驱动,这个驱动程序是跨平台的.如果在Linux等Unix体系操作系统下使用,需要先安装Iodbc这些第三方ODBC标准支援平台. 简单的ASP示例代码:   复制代码 代码如下: <%   dim sql,mysql   set sql = server.createobject("adodb.connection")   mysql="driver={mysql odbc 

Java&nbsp;超详细讲解抽象类与接口的使用

目录 一.抽象类 1.抽象类的语法 2.抽象类的特性 3.抽象类的作用 二.接口 1.接口的概念 2.接口使用 3.接口特性 4.实现多个接口 5.接口间的继承 6.常用的接口 (1)Comparable接口 (2)Cloneable接口 三.Object类 一.抽象类 在Java中,如果一个类被abstract修饰称为抽象类,抽象类中被abstract修饰的方法称为抽象方法,抽象方法不用给出方法体. 1.抽象类的语法 //抽象类:被abstract修饰的类 public abstract cl

Java详细讲解不同版本的接口语法和抽象类与接口的区别

目录 什么是接口? 接口的语法: (JDK7.0) 接口的语法: (JDK8.0) 接口的语法: (JDK9.0)—(私有方法) 接口的分类 常量接口: 空接口: 函数式接口: 什么是接口? 说到接口,USB大家肯定不陌生~接口是一种标准.规范.注意:接口一旦制定好,使用者和实现者都必须遵循的标准. 接口的语法: (JDK7.0) (1) 关键字:interface (2) 语法:  interface 接口名{} (3) 接口编译之后会生成对应的 .class文件 (4) 接口不能创建对象,但

Java超详细讲解接口的实现与用法

目录 1.接口的定义 2.接口的实现 3.接口的引用 4.接口的继承 5.利用接口实现多重继承 1.接口的定义 接口是一种特殊的抽象类,是Java提供的一个重要的功能,与抽象类不同的是: 接口的所有数据成员都是静态的且必须初始化. 接口中的所有方法必须都是抽象方法,不能有一般的方法. [public] interface 接口名称 [extends  父接口名列表]{    [public] [static] [final]数据类型 成员变量名 = 常量;    ...    [public][

Spring详细讲解FactoryBean接口的使用

目录 一.基本使用 二.高级使用 FactoryBean是一个接口,创建对象的过程使用了工厂模式. 一.基本使用 让Spring容器通过FactoryBean来实现对象的创建. 创建FactoryBean案例 public class SimpleFactoryBean implements FactoryBean<Cat> { @Override public Cat getObject() throws Exception { // 创建Cat对象并且返回 return new Cat(&

Java迭代器与Collection接口超详细讲解

目录 关于迭代器你都知道什么? 什么是迭代器? 迭代器的4个API 如何使用迭代器? Collection集合接口知多少? 为什么不使用数组而是集合? Collection接口的API都有什么? AbstractCollection类知多少? 关于迭代器你都知道什么? 什么是迭代器?   所谓迭代的意思就是交换替代,迭代器并不是一种数据结构或者集合,而是可以过迭代器中的方法逐个访问集合中的每个元素的一种方法.提到迭代器最重要的就是Iterator接口,所有想要使用迭代器迭代的结构都需要实现或者继

C++&nbsp;Boost&nbsp;Optional示例超详细讲解

目录 一.概述 二.Boost.Optional 一.概述 数据结构类似于容器,因为它们可以存储一个或多个元素.但是,它们与容器不同,因为它们不支持容器通常支持的操作.例如,使用本部分介绍的数据结构,不可能在一次迭代中访问所有元素. Boost.Optional 可以很容易地标记可选的返回值.使用 Boost.Optional 创建的对象要么是空的,要么包含单个元素.使用 Boost.Optional,您无需使用空指针或 -1 等特殊值来指示函数可能没有返回值. Boost.Tuple 提供了

C++&nbsp;Boost&nbsp;Variant示例超详细讲解

目录 一.提要 二.示例 一.提要 Boost.Variant 提供了一个类似于 union 的名为 boost::variant 的类.您可以将不同类型的值存储在 boost::variant 变量中.在任何时候只能存储一个值.分配新值时,旧值将被覆盖.但是,新值的类型可能与旧值不同.唯一的要求是这些类型必须作为模板参数传递给 boost::variant,这样它们才能为 boost::variant 变量所知. boost::variant 支持任何类型.例如,可以将 std::string