C++ 错题本 MAC环境下 unique_lock try_lock_for函数爆红问题

下方是一个非常简单的,尝试使用unique_lock去尝试加锁的示例代码,在调用try_lock_for函数的时候爆红。这个函数本来就是按照编辑器提示点出来的,不可能没有这个方法 ,比较奇怪。

报错如图所示:

运行的时候编译器报错:no member named 'try_lock_for' in 'std::mutex'

代码如下:

cpp 复制代码
/**
 *如果直接调用 unique_lock<mutex> unique_lock(mtx);
  则会出现直接上锁的操作,因为默认就是加锁。但是我们如果要用到try lock for所指向的功能
  即:如果在拿资源的时候,资源已经被别的线程使用中,则会尝试等待指定的时间,一直获取锁,等到时间到了,代码无论如何都要解除阻塞继续执行。
  则用原先的构造方法难以实现。所以这样用需要用到第二种构造方法,来取消构造时加锁的操作
 */
void UniqueLock::testTryLockFor(){
    unique_lock<mutex> lock(mtx, std::defer_lock); //构造函数执行但是内部不加锁!
    //就给5秒的时间,如果加不了锁,那么5秒过后就会直接执行
    this_thread::sleep_for(chrono::seconds(5));
    // 问题出处,此处 try_lock_for 爆红
    lock.try_lock_for(std::chrono::seconds(5)); 
    for (int i = 0; i < 100000; ++i) {
        b++;
    }
}

问题分析

其实表面上原因解释非常简单,就是没有try_lock_for这个方法。楼主查了一下,这个方法是在C++11标准里添加的,楼主的版本也是C++11。版本上来讲是正确的。所以还得往别的方向查。

然后我们好好看报错内容,点击调用的代码,发现是,unique_lock这个文件中

内部维护的 mutex变量所属的类,它没有try_lock_for这个方法!嗯,没有这个方法,但源码中却有这么一行代码,让惯用java的楼主感觉震惊!总觉得没那么严谨。

然而这个方法用到了一个模板,楼主传入的类型为mutex类型,结果查看,的确,楼主传入的类型并没有实现 try_lock_for 函数。

但是!有的类型实现了!

一个叫 recursive_timed_mutex的类是支持try_lock_for函数的。所以:

将锁的类型改为 recursive_timed_mutex 类型

cpp 复制代码
/**
 *如果直接调用 unique_lock<mutex> unique_lock(mtx);
  则会出现直接上锁的操作,因为默认就是加锁。但是我们如果要用到try lock for所指向的功能
  即:如果在拿资源的时候,资源已经被别的线程使用中,则会尝试等待指定的时间,一直获取锁,等到时间到了,代码无论如何都要解除阻塞继续执行。
  则用原先的构造方法难以实现。所以这样用需要用到第二种构造方法,来取消构造时加锁的操作
 */
void UniqueLock::testTryLockFor(){
    // 解决,将传入的锁改为recursive_timed_mutex类型即可。
    unique_lock<recursive_timed_mutex> lock(recursive_time_mutex, std::defer_lock); //构造函数执行但是内部不加锁!
    //就给5秒的时间,如果加不了锁,那么5秒过后就会直接执行
    this_thread::sleep_for(chrono::seconds(5));
    lock.try_lock_for(std::chrono::seconds(5));
    for (int i = 0; i < 100000; ++i) {
        b++;
    }
}

总结及扩展

楼主是刚系统的学习C++, 是跟着一个视频写的,对于上述代码,视频中,用的的确是C++11, 用到的类的确仅仅就是mutex这个类型,就能全部跑通, 但楼主的代码就是不可以。

为什么呢?

直到楼主看源码注意到一行字,并且查了一下,发现了 LLVM 这个框架的存在。

老师的代码,不知道用的是什么版本的,但楼主的代码,#include 的时候,的确引入的是LLVM框架里的unique_lock 。 这个框架的源码,对mutex代码压根就是没有实现try_lock_for。但是好在它在recursive_timed_mutex类里实现了相关方法。所以才解了。
LLVM是个啥?为什么我的IDE点进去用的是这个库的代码,楼主Clion都没怎么设置。为什么Clion默认设置用LLVM的?

初学C++, 首先感觉,怎么这么乱!!!!

LLVM是什么

苹果公司的软硬件体系基本是用到了LLVM这个套件开发的。是一套用于构建和优化编译器的工具。也是创建一门新语言的利器。LLVM规范了源代码转向机器码这一复杂过程。

编译器

编译器是将人可理解的源代码,转变为可执行二进制文件的工具。

其编译根据代码的编译过程分为三个段, 前端, 中间优化, 后端。理论上讲三个阶段的边界是分明的。 但是实现的时候,却五花八门

下图为编译器三段图:

实际上随着语言种类的增加却实现成了这个鬼样子,下图中的每一条连线,都意味着经历了上图中的三段历程

上图可以看出,如果每个语言都编译成机器码的话,对于语言开发者,要开发一门新的语言,要懂得就是全套的,从看得懂的高级语言,到汇编指令,到机器码全部打通。这种全才还是少的,能在各方面集齐这些人,也是不容易的。 重点是,需要的人多,对事物的理解也不同,中间容易出问题。

如果有个专业的团队做专业的事, 把中间打通形成统一的规则,以后开发任何新兴语言,只需要遵守统一的规则,由这个中间件负责专业的转换成机器码,就稳健的多。

于是如图:

LLVM就干了这么一件事,作为一个中枢适配的存在。LLVM使用了一种语言无关的中间代码来表示高级代码,称为中间表示,也就是图中的 IR。就意味着,许多不同的语言,比如Rust, Ruby都能通过LLVM提供的工具链生成同样的IR,这样的话,就可以使用同一套工具进行分析和优化,之后再转换成某种特定架构的机器码。
所以如果您有兴趣打造自己的编程语言,可以学习一下这个。

LLVM

LLVM项目目前已经发展成为一个巨大的编译器相关的工具集合,全称为 Low Level Virtual Machine 。

想想,如果您的项目有不同的语言组成,编译的时候,可以采用针对语言进行单独编译,也可以用LLVM的特点,将所有支持IR相关协议的语言进行编译,那么这种情况下,LLVM就有优势了。

下图为LLVM对各项语言的支持原理。

其流程均是将各个语言解析为统一的IR, 之后LLVM对根据统一的IR来生成针对不同架构的机器码。

从图中也可以看出,Clang 编译器也是LLVM框架下的重要前端,其支持的语言为 C, C++, Objective-C以及其他语言。

楼主因为是MAC本子,配置的是Xcode软件自带的编译器, Xcode自带的编译器就是Clang编译器,是LLVM架构下的工具链。 所以楼主用clion编辑器编辑的时候,引入的代码是 LLVM架构中的代码。

总结

● C++有的语句,方法找不到,是因为方法根本就没有被实现,原因是编译器所带工具链库,实现方式不同导致,但基本上大差不差,大多能一样调用,出现不同的话,应该会找到替代的方式。

● LLVM是个很强悍的编译工具链,苹果公司有采用这套工具链。Xcode使用的Clang编译器,也是LLVM架构下的。Clang编译器以其友好的错误和警告信息著称,受到开发者喜爱。

相关推荐
赔罪13 分钟前
C 语言变量说明符
c语言·开发语言·c++·学习·算法·objective-c
夫琅禾费米线19 分钟前
JavaScript 中的 Generator 函数及其方法
开发语言·前端·javascript
@小博的博客26 分钟前
C++初阶学习 第十二弹——stack与queue的介绍和使用
开发语言·数据结构·c++·学习
佑冰37 分钟前
C++ 矩阵旋转
数据结构·c++·算法·c
CYRUS STUDIO41 分钟前
编译 LLVM 源码,使用 Clion 调试 clang
c语言·c++·visual studio·clang·ndk·llvm·clion
St_Ludwig1 小时前
C语言小撰特殊篇-assert断言函数
c语言·c++·后端·算法
PaLu-LI1 小时前
ORB-SLAM2源码学习:Initializer.cc:Initializer::Normalize地图初始化——坐标归一化
c++·opencv·学习·算法·ubuntu·计算机视觉
秋说1 小时前
【数据结构 | C++】部落
数据结构·c++·算法
techdashen1 小时前
Go与黑客(第四部分)
开发语言·后端·golang
宇宙大豹发1 小时前
【Python】爬虫实战:高效爬取电影网站信息指南(涵盖了诸多学习内容)
开发语言·爬虫·python·学习·python爬虫·python代码·python使用