Linux 互斥锁
互斥的概念
在多线程编程中,引入了对象互斥锁的概念,来保证共享数据操作的完整性。 每个对象都对应于一个可称为" 互斥锁" 的标记,这个标记用来保证在任一时刻, 只能有一个线程访问该对象。
互斥锁操作
互斥锁也可以叫线程锁,接下来说说互斥锁的的使用方法。
对互斥锁进行操作的函数,常用的有如下几个:
#include <pthread.h> int pthread_mutex_destroy(pthread_mutex_t *mutex);
int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
int pthread_mutex_timedlock(pthread_mutex_t *restrict mutex, const struct timespec *restrict abs_timeout);
对线程锁进行操作的函数有很多,还包括许多线程锁属性的操作函数, 不过一般来说,对于并不复杂的情况, 只需要使用创建、获取锁、释放锁、删除锁这几个就足够了。
创建互斥锁
所以下面简单看一下如何创建和使用互斥锁。
在使用互斥锁之前,需要先创建一个互斥锁的对象。 互斥锁的类型是 pthread_mutex_t ,所以定义一个变量就是创建了一个互斥锁。
pthread_mutex_t mtx;
这就定义了一个互斥锁。但是如果想使用这个互斥锁还是不行的,我们还需要对这个互斥锁进行初始化, 使用函数 pthread_mutex_init() 对互斥锁进行初始化操作。
//第二个参数是 NULL 的话,互斥锁的属性会设置为默认属性
pthread_mutex_init(&mtx, NULL);
除了使用 pthread_mutex_init() 初始化一个互斥锁,我们还可以使用下面的方式定义一个互斥锁:
pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
在头文件 /usr/include/pthread.h 中,对 PTHREAD_MUTEX_INITIALIZER 的声明如下
# define PTHREAD_MUTEX_INITIALIZER \
{ { , , , , , , { , } } }
为什么可以这样初始化呢,因为互斥锁的类型 pthread_mutex_t 是一个联合体, 其声明在文件 /usr/include/bits/pthreadtypes.h 中,代码如下:
/* Data structures for mutex handling. The structure of the attribute
type is not exposed on purpose. */
typedef union
{
struct __pthread_mutex_s
{
int __lock;
unsigned int __count;
int __owner;
#if __WORDSIZE == 64
unsigned int __nusers;
#endif
/* KIND must stay at this position in the structure to maintain
binary compatibility. */
int __kind;
#if __WORDSIZE == 64
int __spins;
__pthread_list_t __list;
# define __PTHREAD_MUTEX_HAVE_PREV
#else
unsigned int __nusers;
__extension__ union
{
int __spins;
__pthread_slist_t __list;
};
#endif
} __data;
char __size[__SIZEOF_PTHREAD_MUTEX_T];
long int __align;
} pthread_mutex_t;
获取互斥锁
接下来是如何使用互斥锁进行互斥操作。在进行互斥操作的时候, 应该先"拿到锁"再执行需要互斥的操作,否则可能会导致多个线程都需要访问的数据结果不一致。 例如在一个线程在试图修改一个变量的时候,另一个线程也试图去修改这个变量, 那就很可能让后修改的这个线程把前面线程所做的修改覆盖了。
下面是获取锁的操作:
阻塞调用
pthread_mutex_lock(&mtx);
这个操作是阻塞调用的,也就是说,如果这个锁此时正在被其它线程占用, 那么 pthread_mutex_lock() 调用会进入到这个锁的排队队列中,并会进入阻塞状态, 直到拿到锁之后才会返回。
非阻塞调用
如果不想阻塞,而是想尝试获取一下,如果锁被占用咱就不用,如果没被占用那就用, 这该怎么实现呢?可以使用 pthread_mutex_trylock() 函数。 这个函数和 pthread_mutex_lock() 用法一样,只不过当请求的锁正在被占用的时候, 不会进入阻塞状态,而是立刻返回,并返回一个错误代码 EBUSY,意思是说, 有其它线程正在使用这个锁。
int err = pthread_mutex_trylock(&mtx);
if( != err) {
if(EBUSY == err) {
//The mutex could not be acquired because it was already locked.
}
}
超时调用
如果不想不断的调用 pthread_mutex_trylock() 来测试互斥锁是否可用, 而是想阻塞调用,但是增加一个超时时间呢,那么可以使用 pthread_mutex_timedlock() 来解决, 其调用方式如下:
struct timespec abs_timeout;
abs_timeout.tv_sec = time(NULL) + ;
abs_timeout.tv_nsec = ; int err = pthread_mutex_timedlock(&mtx, &abs_timeout);
if( != err) {
if(ETIMEDOUT == err) {
//The mutex could not be locked before the specified timeout expired.
}
}
上面代码的意思是,阻塞等待线程锁,但是只等1秒钟,一秒钟后如果还没拿到锁的话, 那就返回,并返回一个错误代码 ETIMEDOUT,意思是超时了。
其中 timespec 定义在头文件 time.h 中,其定义如下
struct timespec
{
__time_t tv_sec; /* Seconds. */
long int tv_nsec; /* Nanoseconds. */
};
还需要注意的是,这个函数里面的时间,是绝对时间,所以这里用 time() 函数返回的时间增加了 1 秒。
释放互斥锁
用完了互斥锁,一定要记得释放,不然下一个想要获得这个锁的线程, 就只能去等着了,如果那个线程很不幸的使用了阻塞等待,那就悲催了。
释放互斥锁比较简单,使用 pthread_mutex_unlock() 即可:
pthread_mutex_unlock(&mtx);
销毁线程锁
通过 man pthread_mutex_destroy 命令可以看到 pthread_mutex_destroy() 函数的说明, 在使用此函数销毁一个线程锁后,线程锁的状态变为"未定义"。有的 pthread_mutex_destroy 实现方式,会使线程锁变为一个不可用的值。一个被销毁的线程锁可以被 pthread_mutex_init() 再次初始化。对被销毁的线程锁进行其它操作,其结果是未定义的。
对一个处于已初始化但未锁定状态的线程锁进行销毁是安全的。尽量避免对一个处于锁定状态的线程锁进行销毁操作。
销毁线程锁的操作如下:
pthread_mutex_destroy(&mtx)
同步地址:https://www.fengbohello.top/archives/linux-pthread-mutex
Linux 互斥锁的更多相关文章
- Linux互斥锁、条件变量和信号量
Linux互斥锁.条件变量和信号量 来自http://kongweile.iteye.com/blog/1155490 http://www.cnblogs.com/qingxia/archive/ ...
- Linux 互斥锁的实现原理(pthread_mutex_t)
本文参考--http://www.bitscn.com/os/linux/201608/725217.html 和http://blog.csdn.net/jianchaolv/article/det ...
- linux 互斥锁和条件变量
为什么有条件变量? 请参看一个线程等待某种事件发生 注意:本文是linux c版本的条件变量和互斥锁(mutex),不是C++的. mutex : mutual exclusion(相互排斥) 1,互 ...
- 详解linux互斥锁 pthread_mutex和条件变量pthread_cond
[cpp] view plaincopy ============================================================= int pthread_creat ...
- linux 2.6 互斥锁的实现-源码分析
http://blog.csdn.net/tq02h2a/article/details/4317211 看了看linux 2.6 kernel的源码,下面结合代码来分析一下在X86体系结构下,互斥锁 ...
- linux多线程编程之互斥锁
多线程并行运行,共享同一种互斥资源时,需要上互斥锁来运行,主要是用到pthread_mutex_lock函数和pthread_mutex_unlock函数对线程进行上锁和解锁 下面是一个例子: #in ...
- Linux内核互斥锁--mutex
一.定义: /linux/include/linux/mutex.h 二.作用及访问规则: 互斥锁主要用于实现内核中的互斥访问功能.内核互斥锁是在原子 API 之上实现的,但这对于内核用户是不可见 ...
- 漫画|Linux 并发、竞态、互斥锁、自旋锁、信号量都是什么鬼?(转)
知乎链接:https://zhuanlan.zhihu.com/p/57354304 1. 锁的由来? 学习linux的时候,肯定会遇到各种和锁相关的知识,有时候自己学好了一点,感觉半桶水的自己已经可 ...
- 非常精简的Linux线程池实现(一)——使用互斥锁和条件变量
线程池的含义跟它的名字一样,就是一个由许多线程组成的池子. 有了线程池,在程序中使用多线程变得简单.我们不用再自己去操心线程的创建.撤销.管理问题,有什么要消耗大量CPU时间的任务通通直接扔到线程池里 ...
随机推荐
- 为表格动态添加一行,miniui组件无效
想要使用miniui实现这样的功能,点击按钮,在一个<td>中动态添加一个miniui输入框和一个按钮,结果miniui的样式无法渲染,请问这种问题可以怎么解决代码如下: <tr&g ...
- android studio使用CMake和NDK,实现应用自身被卸载时打开某一网址
实现应用自身被卸载时打开某一网址的c代码 MyActivity: public class MyActivity extends Activity { /** * Called when the ac ...
- DDL DML概念 --- Msysql常用命令
一.DML DML(data manipulation language)数据操纵语言: 就是我们最经常用到的 SELECT.UPDATE.INSERT.DELETE. 主要用来对数据库的数据进行一些 ...
- Numpy np.array 相关常用操作学习笔记
1.np.array构造函数 用法:np.array([1,2,3,4,5]) 1.1 numpy array 和 python list 有什么区别? 标准Python的列表(list)中,元素本质 ...
- 杭电1024----Max Sum Plus Plus
/* 这题还没有理解透彻.某个dalao也不写注释.只能自己理解了... 先求为i个元素(1<=i<=M)为一个区间的最大和,保证元素个数大于等于i个,递推到M个即可 借鉴原址:http: ...
- C#常用字符串函数
Compare 比较字符串的内容,考虑文化背景(场所),确定某些字符是否相等 CompareOrdinal 与Compare一样,但不考虑文化背景 Format 格式化包含各种值的字符串和如何格式化每 ...
- Java技能提升之路
James收集Java技能提升方面的资料,分为:基础篇.提高篇.高级篇… 给出“入口”,自我提升 1. Java工程师成神之路:http://www.hollischuang.com/archives ...
- FPGA - 认识FPGA
一.何为FPGA? FPGA,英文全拼:Field-Programmable Gate Array 现场可编程门阵列,它是在PAL.GAL.CPLD等可编程器件的基础上进一步发展的产物.它是作为专用集 ...
- AVL树的理解及自写AVL树
AVL树是最先发明的自平衡二叉查找树.在AVL树中任何节点的两个子树的高度最大差别为一,所以它也被称为高度平衡树.查找.插入和删除在平均和最坏情况下都是O(log n).增加和删除可能需要通过一次或多 ...
- [POJ]代码托运站
这里暂时是空的