1、互斥量(Mutex

1.1初始化

互斥量是属于pthread_mutex_t类型变量使用之前必须初始化

初始化方法有两种:静态初始化动态初始化

静态初始pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER

pthread_mutex_t mtx是个结构类型静态初始化时,只能在定义时,不能先定义再用PTHREAD_MUTEX_INITIALIZER初始化。

比如: static pthread_mutex_t mtx;

          Mtx = PTHREAD_MUTEX_INITIALIZER;

编译报错

#include <pthread.h&gt;

int

pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);

                                                                                                                                            成功:0 失败:非0

动态初始pthread_mutex_init

restrict修饰符,用来修饰一个指针通俗讲:只要这个指针活着,我保证这个指针独享这片内存没有‘别人’可以修改这个指针指向的这片内存,所有修改都得通过这个指针来。

原则上,在如下情况,应使用动态初始化:

  1. 动态分配于堆中的互斥量
  2. 互斥量是在栈中分配自动变量
  3. 静态分配,但不使用默认属性的互斥量
  4. 静态分配,但没有定义时候静态初始化的

所以,使用静态初始化的情况如下静态分配变量使用默认属性。包括:全局变量局部static静态变量

如下静态初始

static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

static struct vhost_user vhost_user = {

    .fdset = {

        .fd = { [0 MAX_FDS 1] = {-1, NULL, NULL, NULL, 0} },

        .fd_mutex = PTHREAD_MUTEX_INITIALIZER,

        .num = 0

    },

    .vsocket_cnt = 0,

    .mutex = PTHREAD_MUTEX_INITIALIZER,

};

互斥量属性

通常情况下,使用默认属性即可。静态初始化使用的就是默认属性。

动态初始化函数pthread_mutex_init第二个参数,为NULL时,就是使用默认属性。

截取DPDK源码中的一段

struct vhost_user_reconnect_list {

    struct vhost_user_reconnect_tailq_list head;

    pthread_mutex_t mutex;

};

static int

vhost_user_reconnect_init(void)

{

    int ret;

    pthread_mutex_init(&amp;reconn_list.mutex, NULL);

   

    return ret;

}

1.2 获取和释放

获取互斥量:pthread_mutex_lock(pthread_mutex_t *mutex)

释放互斥量:pthread_mutex_unlock(pthread_mutex_t *mutex);

尝试获取互斥量:pthread_mutex_trylock(pthread_mutex_t *mutex);

#include <pthread.h&gt;

int pthread_mutex_lock(pthread_mutex_t *mutex);

int pthread_mutex_unlock(pthread_mutex_t *mutex);

int pthread_mutex_trylock(pthread_mutex_t *mutex);                                                                                                                                           成功:0 失败:非0   

锁定互斥量,在调用pthread_mutex_lock需要指定互斥量。如果互斥量当前处于未锁定状态,该调用锁定互斥量并立即返回。如果其他线程已经锁定了这一互斥量,那么该调用会一直阻塞,直到该互斥量被解锁

Pthread_mutex_trylock函数调用获取互斥量时,如果互斥量当前处于未锁定状态,跟pthread_mutex_lock一样,锁定互斥量并立即返回。如果其他线程已经锁定这一互斥量,该调用不会阻塞,会返回EBUSY错误

如果发起pthread_mutex_lock调用的线程自身之前已经将目标互斥量锁定,此时会产生什么结果呢?这跟互斥量属性有关。关于Mutex属性我们暂不做过多说明。后续遇到实际情况再补充。这里简单说明一下几个互斥量类型(类型属于属性的一种)。

PTHREAD_MUTEX_NORMAL

         该类型的互斥量不具备死锁自检功能。如线程试图对已经由自己锁定的互斥量加锁,会发生死锁。互斥量处于未锁定状态或者已经由其他线程锁定,对其解锁会导致不确定结果。(Linux上会成功)

PTHREAD_MUTEX_ERRORCHECK

         对此类互斥量的所有操作都会执行错误检查。上面说的几种情况,都会导致函数返回错误。这类互斥量运行起来比一般类型慢,可以作为调试工具用。

PTHREAD_MUTEX_RECURSIVE

         递归互斥量属性。支持线程对已经由自己锁定的互斥量加锁。只要保证加锁次数和解锁的次数匹配即可解锁时如果互斥量处于未锁定状态,或者由其他线程锁定,操作会失败。

在Linux上,PTHREAD_MUTEX_DEFAULT类型互斥量的行为与PTHREAD_MUTEX_NORMAL相似

如果由不止一个线程在等待获取函数pthread_mutex_unlock解锁的互斥量,则无法判断究竟哪个线程将如愿以偿。

1.3 销毁

#include <pthread.h&gt;

int

pthread_mutex_destroy(pthread_mutex_t *restrict mutex);                                                                                                                                          成功:0 失败:非0

这个销毁函数是与动态初始化函数配套使用的。静态初始化的Mutex不需要销毁操作

只有当互斥量处于未锁定状态,且后续也无任何线程企图锁定它时,将其销毁才是安全的。

若互斥量驻留在动态分配的一片内存区域中,应在free内存区域之前将其销毁。对于自动分配的互斥量(一般指局部非静态变量),也应在宿主函数返回前将其销毁。

经由pthread_mutex_detroy销毁的互斥量,可以用pthread_mutex_init对其重新初始化。

1.4死锁

有人的地方就有江湖。有锁的地方,就有死锁风险

一种典型的死锁情况:

线程A和B都成功锁住一个互斥量,接着试图对已经被另一个线程锁定的互斥量加锁。两个线程无限制等待下去。

另一种死锁情况:忘记释放锁。

一般在有goto, return的地方,容易忽略unlock

1.5适用场景

因为Mutex是有名字的,可以找得到。所以互斥量可以应用统一进程不同线程之间,也可以用在不同进程的线程之间,也可以用在进程之间

如果跨进程使用,互斥量需要是进程间共享的。注意条件有:

  1. 需要放在共享内存中,但不能放在reserve预留内存中。
  2. 调用pthread_mutex_setpshared接口设置进程间共享属性。
  3. 一个进程异常退出了,没有释放mutex。怎么办?可以通过设置属性,获取锁时,发现锁被持有并且锁的owner不存在了,就会返回特定错误值EOWNERDEAD。获取错误值后,一系列操作处理错误值:
  1. 首先调用pthread_mutex_consistent_np函数来恢复该锁的一致性
  2. 然后调用pthread_mutex_unlock来解
  3. 接下来在调用加锁

所以,跨进程使用Mutex比较复杂。遇到实际案例再做补充。

原文地址:https://blog.csdn.net/liu1250836704/article/details/134791414

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任

如若转载,请注明出处:http://www.7code.cn/show_43126.html

如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱suwngjj01@126.com进行投诉反馈,一经查实,立即删除

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注