以Linux下的测试程序说明递归型互斥量和普通互斥量的区别
先贴代码和测试结果
// Mutex.h: 对pthread的互斥量的RAII包装
#ifndef _MUTEX_H_
#define _MUTEX_H_ #include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h> // 使用错误码errnum和字符串msg来打印错误信息, 并且退出程序
static inline void errExitEN(int errnum, const char* msg)
{
fprintf(stderr, "%s Error: %s\n", msg, strerror(errnum));
exit();
} class Mutex
{
public:
explicit Mutex()
{
int s;
pthread_mutexattr_t attr;
s = pthread_mutexattr_init(&attr);
if (s != )
errExitEN(s, "pthread_mutexattr_init");
s = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_DEFAULT);
#ifdef ERRORCHECK
s = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK);
#elif RECURSIVE
s = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
#endif
if (s != )
errExitEN(s, "pthread_mutexattr_settype"); pthread_mutex_init(&__mtx, &attr);
if (s != )
errExitEN(s, "pthread_mutex_init"); s = pthread_mutexattr_destroy(&attr);
if (s != )
errExitEN(s, "pthread_mutexattr_destroy");
} virtual ~Mutex()
{
int s = pthread_mutex_destroy(&__mtx);
if (s != )
errExitEN(s, "pthread_mutex_destroy");
} void lock()
{
int s = pthread_mutex_lock(&__mtx);
if (s != )
errExitEN(s, "pthread_mutex_lock");
} void unlock()
{
int s = pthread_mutex_unlock(&__mtx);
if (s != )
errExitEN(s, "pthread_mutex_unlock");
}
private:
pthread_mutex_t __mtx;
}; #endif
// MutexTest.cpp: Mutex类对于重复获取同一把锁的测试
#include "Mutex.h"
#include <stdio.h>
#include <pthread.h>
#include <array> Mutex mtx;
std::array<int, > g_array; // 将g_array[index]左边的元素自增(使用互斥量来保护)
void incrLeftWithMutex(int index)
{
mtx.lock();
for (int i = ; i < index; i++)
g_array[i]++;
mtx.unlock();
} // 将g_array[index]右边的元素自增(使用互斥量来保护)
void incrRightWithMutex(int index)
{
mtx.lock();
for (int i = index + ; i < (int) g_array.size(); i++)
g_array[i]++;
mtx.unlock();
} // 将g_array[index]以外的元素自增
void incrOtherItem(int index)
{
mtx.lock();
incrLeftWithMutex(index);
incrRightWithMutex(index);
mtx.unlock();
} int main()
{
g_array.fill();
incrOtherItem(); for (int i : g_array)
printf("%d ", i);
printf("\n");
return ;
}
$ g++ MutexTest.cpp -std=c++ -pthread
$ time ./a.out
^C
real 0m3.973s
user 0m0.004s
sys 0m0.000s $ g++ MutexTest.cpp -std=c++ -pthread -DERRORCHECK
$ ./a.out
pthread_mutex_lock Error: Resource deadlock avoided
pthread_mutex_destroy Error: Device or resource busy
$ g++ MutexTest.cpp -std=c++ -pthread -DRECURSIVE
$ ./a.out
不额外定义宏则使用默认的互斥量(锁),定义宏ERRORCHECK则锁自带错误检查,定义宏RECURSIVE则代表递归锁。
主线程中调用了incrOtherItem函数,该函数先获取(acquire)锁mtx,然后调用另外2个函数后释放(release)锁mtx。
实验结果显示默认锁陷入了死锁,错误检查的结果是resource deadlock avoided(即陷入了死锁),而递归锁则成功执行了下去。
因为向一把已经被获取的锁申请上锁时,线程会阻塞一直到已获取锁的一方将锁释放。所以若线程已经获取了锁A而未释放,当它再次获取锁A时会陷入死锁,因为此线程会阻塞直到锁A被释放,然后只有拥有锁的线程(也就是它自己)才能释放锁,而线程自己处于阻塞中,所以永远处于阻塞状态。
递归锁就是为了解决这种状况,从incrOtherItem的函数定义看起来代码没任何问题,但是incrLeftWithMutex和incrRightWithMutex函数试图获取了同一把锁,这样相当于未释放锁就再次获取同一把锁。
递归锁会在内部维护一个计数器,当线程第1次获取互斥量时,计数器置为1,之后该线程可以在此获取同一把锁,每次获取锁计数器加1,每次释放锁计数器减1。由于此时其他线程无法获取锁,所以只要保证该线程的执行过程是可重入的,代码就没问题。
由于这种情况往往是函数递归调用时才出现的,比如
函数1:上锁,调用函数2,解锁。
函数2:上锁……解锁
函数1的过程就变成了:上锁,函数1的内容(第一部分),上锁,函数2的内容,解锁,函数1的内容(第二部分),解锁。
如果函数1的内容是不可重入的,而函数2修改了函数1的操作对象,那么这里就会出问题。
比如。函数1是想获取全局int数组(设为int a[4] = { 1,2,3,4 } )的总和,第一部分是求前半部分的和,第二部分是求后半部分的和。
而函数2若导致int数组发生了变化,比如让a[2] = 0,这样最后求得的和就是1+2+0+4=7而不是1+2+3+4=10。
以Linux下的测试程序说明递归型互斥量和普通互斥量的区别的更多相关文章
- Linux下SPI测试程序
/** 说明:SPI通讯实现* 方式一: 同时发送与接收实现函数: SPI_Transfer()* 方式二:发送与接收分开来实现* SPI_Write() 只发送* SPI_Read() 只接收* 两 ...
- linux下串口测试程序
通过简单的参数配置,执行文件+串口号+波特率 #include <stdio.h> #include <stdlib.h> #include <unistd.h> ...
- linux下使用mv将递归的文件从多个目录移动到一个目录中
find /data/download/temp \( -iname '*.mp4' -o -iname '*.avi' \) -type f -exec mv -nv -t '/data/downl ...
- Linux下查找文件:which、whereis、locate、find 命令的区别
我们经常在linux要查找某个文件,但不知道放在哪里了,可以使用下面的一些命令来搜索.which 查看可执行文件的位置,通过环境变量查whereis 查看文件的位置,通过数据库查,每 ...
- linux下的so、o、lo、a、la文件的区别
o: 编译的目标文件a: 静态库,其实就是把若干o文件打了个包so: 动态链接库(共享库) lo: 使用libtool编译出的目标文件,其实就是在o文件中添加了一些信息la: 使用libtool编译出 ...
- linux下C++遍历文件夹下的全部文件;Windows/Linux下C++批量修改文件名,批量删除文件
Linux下 C++遍历目录下所有文件 rename(image_path.c_str(), image_path_new.c_str()); remove(image_path_move.c_str ...
- Linux下Centos 7如何关闭防火墙?
Linux下的防火墙有两种:Iptables和Firewall(概念以及区别大家可以自行搜索).为什么要关闭防火墙呢?主要是我们都过Linux搭建服务器的时候其他机器访问会被墙掉,例如:Tomcat, ...
- linux下C++ STL hash_map的使用以及使用char *型变量作为Key值的一大“坑”
计算机编程中经常会用到hash表,而在C++中,使用STL编程更是少不了的.本文将介绍STL中hash_map的使用.在hash_map中使用自定义类型作为key值的方法以及在使用char *类型作为 ...
- linux下递归列出目录下的所有文件名(不包括目录)
1.linux下递归列出目录下的所有文件名(不包括目录) ls -lR |grep -v ^d|awk '{print $9}'2.linux下递归列出目录下的所有文件名(不包括目录),并且去掉空行 ...
随机推荐
- 登录后保存token值到cookie中
1.引入相应JS <script src="web/js/jquery-1.9.1.min.js"></script> <script src=&qu ...
- BGP&RIP
策略路由是2000之前 如果不通检查是否有相互影响的策略 BGP 25端 查看 25是上端起lan 地址是172.100.1.254 互联158是下端起wan 172.100.1.158 25的内 ...
- RabbitMQ(6) 集群部署
单节点部署 rabbitmq单节点部署比较简单,可以使用apt-get等工具快速安装部署. wget -O- https://www.rabbitmq.com/rabbitmq-release-sig ...
- Django知识点总结
一.什么是web框架? 框架,即framework,特指为解决一个开放性问题而设计的具有一定约束性的支撑结构,使用框架可以帮你快速开发特定的系统,简单地说,就是你用别人搭建好的舞台来做表演. web应 ...
- C++面向对象高级编程(四)基础篇
技术在于交流.沟通,转载请注明出处并保持作品的完整性. 一.Static 二.模板类和模板函数 三.namespace 一.Static 静态成员是“类级别”的,也就是它和类的地位等同,而普通成员是“ ...
- 我也说说Emacs吧(5) - 基本编辑操作
基本编辑操作 进入编辑模式 标准的emacs用户是遇不到这一节的,因为默认就可以编辑.但是spacemacs用户需要先学习一下强大的vi的模式切换功能了. vi的一个重要特点就是命令特别多,所以一旦学 ...
- UCB算法
前言: 来万物花开这家创业公司实习,也真是一波三折.先实习了三天,每天下午到公司工作到晚上.工作时间是每天下午到晚上9.30.结果每天上午没法用心干实验室的活了,下午在公司工作的时候,总是提心吊胆,手 ...
- UE4 引擎基础类说明
本文章由cartzhang编写,转载请注明出处. 所有权利保留. 文章链接:http://blog.csdn.net/cartzhang/article/details/76048437 作者:car ...
- 上周面试回来后写的Java面试总结,想进BAT必看
上周陪同之前一起工作的同事去面试(乔治,小袁,鹏飞(面试人)),第一站是去深圳,第二站上海,第三站杭州.面试什么公司我在这里就不多说了,你们知道是一线公司就行.其实本来真的没打算写这篇文章,主要是自己 ...
- 【排序】堆排序,C++实现
原创文章,转载请注明出处! 博客文章索引地址 博客文章中代码的github地址 # 预备知识 堆是一种特殊的树形数据结构,即完全二叉树.堆分为大根堆和小根堆,大根堆为根节点的值大于两个子节点的 ...