目录

前言

前面四部分内容已经把目前常用的C++标准库中线程库的一些同步库介绍完成了,这一次我们探讨的都是C++20中的内容。主要有两个部分,信号量和latch与barrier。

由于GCC使用的libstdc++还没有完成这一部分特性,所以我们使用的是LLVM的libc++来进行实验,鉴于gcc更换标准库比较麻烦,所以我们使用的是clang编译器。在编译的时候添加选项-stdlib=libc++ -std=gnu++2a

信号量

对于信号量,我使用的比较少,大部分的时候都是在准备面试的时候,能够看到它的存在。其使用也非常的简单,感觉和锁的使用类似,都是限制更少了。他的作用是保证只有指定数目的执行体可以访问特定的资源。对于信号两,在C++中有两个类分别是std::counting_semaphorestd::binary_semaphore,他们之间的区别比较小。简单来说,std::counting_semaphore<1>就是等于std::binary_semaphore

counting_semaphore

std::counting_semaphore是一个模板,目标有一个类型为std::ptrdiff_t(两个指针的差值,不小于17位的整数),这个不是初始的值而是运行时的最大值。构造函数可以传递初始的信号值。

  1. std::counting_semaphore<> sema(4);

使用的方法有两个acquire()release(ptrdiff_t update = 1),从名字上也明白大致的使用方法。acquire()用于获取一个资源,release()用于释放资源,可以释放多个,但是总数不能超过最大值。

运行的函数:

  1. int thread_fun(int thread_id) {
  2. sema.acquire();
  3. printf("Thread Id %d Get.\n", thread_id);
  4. std::this_thread::sleep_for(1s);
  5. sema.release();
  6. printf("Thread Id %d Release.\n", thread_id);
  7. return 0;
  8. }

初始化10个线程,可以得到类似于下图的结果:

除了阻塞的acquire()之外,还有非阻塞的try_acquire与超时的try_acquire_fortry_acquire_for

latch与barrier

std::latchstd::barrier的作用有点像起跑线,但是两者也有一定的区别。

std::latch只能用一次,如果它的计数器已经到达0,不会复原,再有线程到达,将不会阻塞。而std::barrier在计数器到达0后会复原,可以重复使用。

在使用std::latch时,可以一次性减少多次计数器,而std::barrier只能减少一次。

std::barrier是一个模板,在构造的时候可以传入一个函数类型,在计数器达到零时会执行实例化时传入函数(执行减少计数器的线程)。

latch

std::latch的构造方法很简单,即

  1. constexpr explicit latch( std::ptrdiff_t expected );

expected即需要等待的数量。

使用的方法有四个,分别是

  1. void count_down( std::ptrdiff_t n = 1 );
  2. bool try_wait() const noexcept;
  3. void wait() const;
  4. void arrive_and_wait( std::ptrdiff_t n = 1 );

count_down是将计数器减少n但是不等待,如果n大于内部的计数器,则行为未定义;

try_wait测试是否需要等待;

wait等待计数器减到0;

arrive_and_wait等于先count_downwait

barrier

std::barrier是一个模板,在构造的时候可以传入一个函数类型,在计数器达到零时会执行实例化时传入函数。其构造函数为

  1. constexpr explicit barrier( std::ptrdiff_t expected,
  2. CompletionFunction f = CompletionFunction());

使用的方法也有四个,分别是:

  1. arrival_token arrive( std::ptrdiff_t n = 1 );
  2. void wait( arrival_token&& arrival ) const;
  3. void arrive_and_wait();
  4. void arrive_and_drop();

需要注意的是arrival_token类型,由于barrier可以使用多次,所以如果为了区分不同的阻塞,使用了arrival_token,同一批调用arrive将会得到相同的arrival_token

在调用wait时,如果传入的arrival不为当前的批次,则将直接返回。否则等待该批次的计数器降为0。

arrive_and_drop将会减少计数器并且减少之后复原的计数器。

总结

以上就是C++中的信号量,latchbarrier。使用起来比较简单,但是比较奇怪的是,latchbarrier中大部分的方法名都是相似的,或者说是相同的命名逻辑,但是不阻塞的减少在latch中是count_down,而barrier中为arrive。虽然有可能因为latch中可以传入减少的次数,然而类似的arrive_and_wait的命名却相同。

在更换库的时候,还发现了很多奇葩的地方。当时,我想使用Boost的timer库来计时,timer库分为两个版本,一个是已经被弃用的header only库,可以直接引用。另一个推荐使用的库,需要链接动态库。然而问题就在于此。原来的Boost使用的是libstdc++作为标准库编译的,而我更换了标准库后,编译阶段是没有问题的,但是在链接的时候,提示

  1. undefined reference to `boost::timer::format(boost::timer::cpu_times const&, short, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)'

如果对于编译比较熟悉的话,应该是知道这是在链接时找不到符号导致的错误,一般是没有链接动态库导致的错误。而比较奇怪的是,提示的只有这一个函数,如果是没有链接相关的动态库,应该是有更多的提示,而不是仅仅只有这一条。然后想到更换了标准库,猜想应该是std::string在编译时符号不一致导致的。

经过验证的确如此。通过对以下文件分别使用不同的库进行编译。

  1. #include <thread>
  2. #include <iostream>
  3. #include <string>
  4. #include <chrono>
  5. #include <vector>
  6. #include <boost/timer/timer.hpp>
  7. using namespace std::chrono_literals;
  8. boost::timer::auto_cpu_timer timer;
  9. int my_func(std::string data);
  10. int main() {
  11. my_func("test");
  12. return 0;
  13. }

得到的报错分别是:

libc++

  1. undefined reference to `my_func(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >)'

而libstdc++

  1. undefined reference to `my_func(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >)'

可以看到差别,如果编译成汇编文件,还可以看到更加详细的差别。总之,不同的标准库无法简单的混编。也许Modules能解决这一问题吧。

博客原文:https://www.cnblogs.com/ink19/p/std_thread-5.html

std::thread线程库详解(5)的更多相关文章

  1. std::thread线程库详解(2)

    目录 目录 简介 最基本的锁 std::mutex 使用 方法和属性 递归锁 std::recursive_mutex 共享锁 std::shared_mutex (C++17) 带超时的锁 总结 简 ...

  2. std::thread线程库详解(3)

    目录 目录 前言 lock_guard scoped_lock (C++17) unique_lock shared_lock 总结 ref 前言 前两篇的博文分别介绍了标准库里面的线程和锁,这一次的 ...

  3. std::thread线程库详解(4)

    目录 目录 前言 条件变量 一些需要注意的地方 总结 前言 本文主要介绍了多线程中的条件变量,条件变量在多线程同步中用的也比较多.我第一次接触到条件变量的时候是在完成一个多线程队列的时候.条件变量用在 ...

  4. Java Thread(线程)案例详解sleep和wait的区别

    上次对Java Thread有了总体的概述与总结,当然大多都是理论上的,这次我将详解Thread中两个常用且容易疑惑的方法.并通过实例代码进行解疑... F区别 sleep()方法 sleep()使当 ...

  5. Thread线程相关方法详解

    1.sleep() 使当前线程(即调用该方法的线程)暂停执行一段时间,让其他线程有机会继续执行,但它并不释放对象锁.也就是说如果有synchronized同步快,其他线程仍然不能访问共享数据.注意该方 ...

  6. “全栈2019”Java多线程第十章:Thread.State线程状态详解

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...

  7. Lua的协程和协程库详解

    我们首先介绍一下什么是协程.然后详细介绍一下coroutine库,然后介绍一下协程的简单用法,最后介绍一下协程的复杂用法. 一.协程是什么? (1)线程 首先复习一下多线程.我们都知道线程——Thre ...

  8. Python--urllib3库详解1

    Python--urllib3库详解1 Urllib3是一个功能强大,条理清晰,用于HTTP客户端的Python库,许多Python的原生系统已经开始使用urllib3.Urllib3提供了很多pyt ...

  9. MySQL5.6的4个自带库详解

    MySQL5.6的4个自带库详解 1.information_schema详细介绍: information_schema数据库是MySQL自带的,它提供了访问数据库元数据的方式.什么是元数据呢?元数 ...

随机推荐

  1. how to overwrite css !important style

    how to overwrite css !important style css !important bug how to override css !important style https: ...

  2. ts 修改readonly参数

    readonly name = "xxx"; updateValueAndValidity(): void { // this.name = 'a'; (this as { nam ...

  3. 交易所频频跑路?Baccarat去中心化交易平台助力资金安全

    过去,黑客攻击可能是交易所跑路的最常见原因.自OKEx事件以来,这些交易所暂停提币或跑路还多了一个原因,就是因创始人正在协助调查. 据不完全统计,自OKEx于10月16日宣布暂停提币后不到两个月,已经 ...

  4. 联童科技基于incubator-dolphinscheduler从0到1构建大数据调度平台之路

    联童科技是一家智能化母婴童产业平台,从事母婴童行业以及互联网技术多年,拥有丰富的母婴门店运营和系统开发经验,在会员经营和商品经营方面,能够围绕会员需求,深入场景,更贴近合作伙伴和消费者,提供最优服务产 ...

  5. 深入浅出的JS执行机制(图文教程)

    前序 作为一个有理想有抱负的前端攻城狮,想要走向人生巅峰,我们必须将我们使用的功法练到天人合一的地步.我在们日常工作中,使用最多的语言就是JavaScript了,为了写出完美的.能装逼的代码,我们必须 ...

  6. Golang 实现 Redis(9): 使用GeoHash 搜索附近的人

    本文是使用 golang 实现 redis 系列的第九篇,主要介绍如何使用 GeoHash 实现搜索附近的人. 搜索附近的POI是一个非常常见的功能,它的技术难点在于地理位置是二维的(经纬度)而我们常 ...

  7. 微信小程序:app.json中通过使用扩展库userExtendedLib的方式,引入并使用weui

    微信小程序  PK  APP: 1.微信有海量⽤⼾,⽽且粘性很⾼,在微信⾥开发产品更容易触达⽤⼾:而推⼴app的成本太⾼. 2.微信小程序也可以跨平台(Android和IOS). 一.project. ...

  8. Jquery获取链接请求的参数

    比如有一个链接:https://www.baidu.com/s?cl=3&tn=baidutop10&fr=top1000,先定义方法: //获取url中的参数 function ge ...

  9. 若依管理系统RuoYi-Vue(三):代码生成器原理和实战

    历史文章 若依管理系统RuoYi-Vue(一):项目启动和菜单创建 若依管理系统RuoYi-Vue(二):权限系统设计详解 本篇文章将会讲解ruoyi-vue系统下代码生成器的使用.原理分析以及将这部 ...

  10. docker启动ubuntu的桌面环境

    一.概述 由于最近一段时间在家办公,国内服务器在阿里云,国外站点在aws.家里的移动宽带比较差,无法访问aws. 所以尝试在阿里云启动docker,找到一个lxde桌面环境的ubuntu镜像. 二.启 ...