body, table{font-family: 微软雅黑; font-size: 10pt}
table{border-collapse: collapse; border: solid gray; border-width: 2px 0 2px 0;}
th{border: 1px solid gray; padding: 4px; background-color: #DDD;}
td{border: 1px solid gray; padding: 4px;}
tr:nth-child(2n){background-color: #f8f8f8;}

1、线程互斥
     
    在Posix Thread中定义了一套专门用于线程互斥的 mutex 函数。mutex 是一种简单的加锁的方法来控制对共享资源的存取,这个互斥锁只有两种状态(上锁和解锁),可以把互斥锁看作某种意义上的全局变量。为什么需要加锁,就是因为多个线程共用进程的资源,要访问的是公共区间时(全局变量),当一个线程访问的时候,需要加上锁以防止另外的线程对它进行访问,实现资源的独占。在一个时刻只能有一个线程掌握某个互斥锁,拥有上锁状态的线程能够对共享资源进行操作。若其他线程希望上锁一个已经上锁了的互斥锁,则该线程就会挂起,直到上锁的线程释放掉互斥锁为止。
Linux 锁类型:
→自旋锁 spinlock (不停的判断锁是否可用,是内核里面的,效率更高,但是占用CPU资源;用户态不提供这个接口)
→互斥锁(睡眠锁) 信号量,mutex    (优势就是节省CPU资源,但是效率不高,存在睡眠和唤醒这个过程)

创建和销毁锁:
有两种方法创建互斥锁,静态方式 和 动态方式

静态方式:(一般不用)

POSIX定义了一个宏 PTHREAD_MUTEX_INITIALIZER 来静态初始化互斥锁,方法如下:

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

在Linux Threads实现中,pthread_mutex_t是一个结构,而PTHREAD_MUTEX_INITIALIZER则是一个宏常量。

动态方式:(常用)

采用pthread_mutex_init()函数来初始化互斥锁,API定义如下:

#include <pthread.h>

pthread_mutex_t mutex;

int  pthread_mutex_init(pthread_mutex_t *mutex, const
pthread_mutexattr_t *mutexattr)

其中 mutexattr 用于指定互斥锁属性(见下),如果为NULL则使用缺省属性。通常为NULL

pthread_mutex_destroy()用于注销一个互斥锁,API定义如下:

int  pthread_mutex_destroy(pthread_mutex_t  *mutex);

销毁一个互斥锁即意味着释放它所占用的资源,且要求锁当前处于开放状态由于在Linux中,互斥锁并不占用任何资源,因此Linux Threads中的pthread_mutex_destroy()除了检查锁状态以外(锁定状态则返回EBUSY)没有其他动作。

锁操作:

锁操作主要包括

加锁             int pthread_mutex_lock(pthread_mutex_t* mutex)

解锁             int pthread_mutex_unlock(pthread_mutex_t* mutex)

测试加锁         int
pthread_mutex_trylock(pthread_mutex_t* mutex)

pthread_mutex_lock :加锁,不论哪种类型的锁,都不可能被两个不同的线程同时得到,而必须等待解锁。对于普通锁类型,解锁者可以是同进程内任何线程;而检错锁则必须由加锁者解锁才有效,否则返回EPERM;对于嵌套锁,文档和实现要求必须由加锁者解锁,但实验结果表明并没有这种限制,这个不同目前还没有得到解释。在同一进程中的线程,如果加锁后没有解锁,则任何其他线程都无法再获得锁。
thread_mutex_unlock :解锁,根据不同的锁类型,实现不同的行为:
对于快速锁,pthread_mutex_unlock解除锁定;
对于递规锁,pthread_mutex_unlock使锁上的引用计数减1;
对于检错锁,如果锁是当前线程锁定的,则解除锁定,否则什么也不做。
pthread_mutex_trylock :语义与pthread_mutex_lock()类似,不同的是在锁已经被占据时返回EBUSY而不是挂起等待。
pthread_mutex_trylock:语义与pthread_mutex_lock()类似,不同的是在锁已经被占据时返回EBUSY而不是挂起等待。

如果线程在加锁后解锁前被取消,锁将永远保持锁定状态,因此如果在关键区段内有取消点存在,则必须在退出回调函数pthread_cleanup_push/pthread_cleanup_pop中解锁。同时不应该在信号处理函数中使用互斥锁,否则容易造成死锁。

pthread_mutex.c
#include <pthread.h>
#include <stdio.h>
//mutex锁的初始化和销毁
int main()
{
        pthread_mutex_t mutex1;
        int ret;
        ret=pthread_mutex_init(&mutex1,NULL);
        //pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
//静态方式,必须这么写
        if(ret!=0)
        {
                printf("pthread_mutex_init failed ret=%d\n",ret);
                return -1;
        }
        printf("mutex init success\n");
        ret=pthread_mutex_destroy(&mutex1);
        if(ret!=0)
        {
                printf("pthread_mutex_destroy failed ret=%d\n",ret);
                return -1;
        }
        return 0;
}
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_add.c
#include<pthread.h>
#include<stdio.h>
pthread_mutex_t m1;
int t=0;
void* thread(void* p)
{
        int i;
        for(i=0;i<20000000;i++)
        {
                pthread_mutex_lock(&m1);
                t++;
                pthread_mutex_unlock(&m1);
        }
        pthread_exit(NULL);
}
int main()
{
        pthread_t pth_id;
        int ret;
        ret=pthread_mutex_init(&m1,NULL);
        if(ret!=0)
        {
                printf("error mutex\n");
                return -1;
        }
        ret=pthread_create(&pth_id,NULL,thread,NULL);
          if(ret!=0)
        {
                printf("error create\n");
                return -1;
        }
        int i;
        for(i=0;i<20000000;i++)
        {
                pthread_mutex_lock(&m1);
                t++;
                pthread_mutex_unlock(&m1);
        }
        pthread_join(pth_id,NULL);
        printf("final t = %d\n",t);
        pthread_mutex_destroy(&m1);
        return 0;
}

//try尝试加锁失败

pthread_mutex_trylock.c
#include<pthread.h>
#include<stdio.h>
#include<unistd.h>
pthread_mutex_t m1;
void* thread(void* p)
{
        pthread_mutex_lock(&m1);
        printf("I get lock\n");
        while(1);
        pthread_exit(NULL);
}
int main()
{
        pthread_t pth_id;
        int ret;
        ret=pthread_mutex_init(&m1,NULL);
        if(ret!=0)
        {
                printf("error mutex_init\n");
                return -1;
        }
        pthread_create(&pth_id,NULL,thread,NULL);
        sleep(1);
        ret=pthread_mutex_trylock(&m1);
        printf("pthread_mutex_trylock ret=%d\n",ret);
        pthread_mutex_destroy(&m1);
        return 0;
}

#define EBUSY 16          /* Device or resource busy */

互斥锁属性:

互斥锁属性结构体的定义为:

typedef struct
{
    int __mutexkind;         //注意这里是两个下划线
}pthread_mutexattr_t;    //这个是过时的了,现在改了,直接用接口赋值

互斥锁的属性在创建锁的时候指定,在LinuxThreads实现中仅有一个锁类型属性__mutexkind,不同的锁类型在试图对一个已经被锁定的互斥锁加锁时表现不同也就是是否阻塞等待。有三个值可供选择:

PTHREAD_MUTEX_TIMED_NP ,这是缺省值(直接写NULL就是表示这个缺省值),也就是普通锁(或快速锁)。当一个线程加锁以后,其余请求锁的线程将形成一个阻塞等待队列,并在解锁后按优先级获得锁。这种锁策略保证了资源分配的公平性
PTHREAD_MUTEX_RECURSIVE_NP 嵌套锁,允许同一个线程对同一个锁成功获得多次,并通过多次unlock解锁。如果是不同线程请求,则在加锁线程解锁时重新竞争。
PTHREAD_MUTEX_ERRORCHECK_NP 检错锁,如果同一个线程请求同一个锁,则返回EDEADLK,否则与PTHREAD_MUTEX_TIMED_NP类型动作相同。这样就保证当不允许多次加锁时不会出现最简单情况下的死锁。如果锁的类型是快速锁,一个线程加锁之后,又加锁,则此时就是死锁。
//初始化一个嵌套锁
pthread_mutex_t  lock;
pthread_mutexattr_t  mutexattr;
pthread_mutexattr_settype(&mutexattr,PTHREAD_MUTEX_RECURSIVE_NP);
pthread_mutex_init(&lock, &mutexattr);
//初始化一个检错锁
pthread_mutex_t  lock;
pthread_mutexattr_t  mutexattr;
pthread_mutexattr_settype(&mutexattr,PTHREAD_MUTEX_ERRORCHECK_NP);
pthread_mutex_init(&lock, &mutexattr);
//初始化一个普通锁

pthread_mutex_t  lock;
//pthread_mutexattr_t  mutexattr;
//pthread_mutexattr_settype(&mutexattr,PTHREAD_MUTEX_TIMED_NP);
pthread_mutex_init(&lock, NULL);
pthread_mutex_lock_attr_recursive_np.c      //嵌套锁 pthread_mutex_lock_attr_errorcheck_np.c     //检错锁 pthread_mutex_timed_np.c      //普通锁
#include<pthread.h>
#include<stdio.h>
int main()
{
        pthread_mutex_t m1;
        pthread_mutexattr_t matter;
        int ret;
        ret=pthread_mutexattr_settype(&matter,PTHREAD_MUTEX_RECURSIVE_NP);
        if(ret!=0)
        {
                printf("error settype\n");
                return -1;
        }
        ret=pthread_mutex_init(&m1,&matter);
        if(ret!=0)
        {
                printf("error init\n");
                return -1;
        }
        pthread_mutex_lock(&m1);
        printf("I lock success\n");
        ret=pthread_mutex_lock(&m1);
        printf("ret=%d\n",ret);
        printf("I lock twice\n");
        return 0;
}
#include<pthread.h>
#include<stdio.h>
int main()
{
        pthread_mutex_t m1;
        pthread_mutexattr_t matter;
        int ret;
        ret=pthread_mutexattr_settype(&matter,PTHREAD_MUTEX_ERRORCHECK_NP);
        if(ret!=0)
        {
                printf("error settype\n");
                return -1;
        }
        ret=pthread_mutex_init(&m1,&matter);
        if(ret!=0)
        {
                printf("error init\n");
                return -1;
        }
        pthread_mutex_lock(&m1);
        printf("I lock success\n");
        ret=pthread_mutex_lock(&m1);  //第二次加锁不会失败,但是会返回错误码
        printf("ret=%d\n",ret);
        printf("I lock twice\n");
        return 0;
}
#include <pthread.h>
#include <stdio.h>
//单个线程加锁两次,第二次加锁进程会死锁
int main()
{
        pthread_mutex_t m1;
        int ret;
        ret=pthread_mutex_init(&m1,NULL);
        if(ret!=0)
        {
                printf("pthread_mutex_init failed,ret=%d\n",ret);
                return -1;
        }
        pthread_mutex_lock(&m1);
        printf("I lock success\n");
        pthread_mutex_lock(&m1);  //第二次加锁,产生死锁
        printf("I lock twice\n");
        return 0;
}

如果不知道设置锁属性的函数,可以直接暴力设置:
pthread_mutexattr_t mutexattr;
int i = PTHREAD_MUTEX_RECURSIVE_NP;
memcpy(&mutexattr,&i,sizeof(int));
查找LINUX库文件里结构体的定义:
find /usr/include/ -name *.h | xargs grep pthread_mutexattr_t
因为是 typedef 出来的,所以看到大括号就是我们要找的,直接 vim 绝对路径  查看:

加锁注意事项

如果线程在加锁后解锁前被取消,锁将永远保持锁定状态,因此如果在关键区段内有取消点存在,则必须在退出回调函数 pthread_cleanup_push/pthread_cleanup_pop 中解锁。同时不应该在信号处理函数中使用互斥锁,否则容易造成死锁

线程互斥,mutex的更多相关文章

  1. MFC线程(三):线程同步事件(event)与互斥(mutex)

    前面讲了临界区可以用来达到线程同步.而事件(event)与互斥(mutex)也同样可以做到. Win32 API中的线程事件 HANDLE hEvent = NULL; void MainTestFu ...

  2. 线程互斥与析构函数中mutex的销毁

    正在实现一个线程池的pthread包装器,突然发现有人在讨论关于http://blog.csdn.net/Solstice/article/details/5238671 是一篇比较老的文章,考虑了下 ...

  3. 【原】iOS多线程之线程间通信和线程互斥

    线程间通信 1> 线程间通信分为两种 主线程进入子线程(前面的方法都可以) 子线程回到主线程 2> 返回主线程 3> 代码 这个案例的思路是:当我触摸屏幕时,会在子线程加载图片,然后 ...

  4. CreateThread创建线程 互斥量锁

    HANDLE CreateThread( LPSECURITY_ATTRIBUTES lpThreadAttributes,//SD:线程安全相关的属性,常置为NULL SIZE_T dwStackS ...

  5. Python 35 线程(2)线程特性、守护线程、线程互斥锁

    一:线程特性介绍 from threading import Thread import time n=100 def task(): global n n=0 if __name__ == '__m ...

  6. UDP、线程、mutex锁(day15)

    一.基于UDP的网络编程模型 服务器端 .创建socket. .将fd和服务器的ip地址和端口号绑定 .recvfrom阻塞等待接收客户端数据 .业务处理 .响应客户端 客户端: .创建socket ...

  7. 8.12 day31 进程间通信 Queue队列使用 生产者消费者模型 线程理论 创建及对象属性方法 线程互斥锁 守护线程

    进程补充 进程通信 要想实现进程间通信,可以用管道或者队列 队列比管道更好用(队列自带管道和锁) 管道和队列的共同特点:数据只有一份,取完就没了 无法重复获取用一份数据 队列特点:先进先出 堆栈特点: ...

  8. 【Java并发专题之三】Java线程互斥、协作原理

    (I)Java线程互斥原理之synchronized原理 从JDK5引入CAS原子操作,但没有对synchronized关键字做优化,而是增加了J.U.C.concurrent,concurrent包 ...

  9. python 之 并发编程(守护线程与守护进程的区别、线程互斥锁、死锁现象与递归锁、信号量、GIL全局解释器锁)

    9.94 守护线程与守护进程的区别 1.对主进程来说,运行完毕指的是主进程代码运行完毕2.对主线程来说,运行完毕指的是主线程所在的进程内所有非守护线程统统运行完毕,主线程才算运行完毕​详细解释:1.主 ...

随机推荐

  1. 【开发者笔记】python

    题记: 最近做Python导入接口,用到xlrd包读取excel文件信息入库,获取合并单元格信息时遇到时而成功时而失败的情况,一开始用xls文件读取不了合并单元格信息,后来换用xlsx格式可以读取.但 ...

  2. Status Code状态码详解对照表

    状态码 含义 100 客户端应当继续发送请求.这个临时响应是用来通知客户端它的部分请求已经被服务器接收,且仍未被拒绝.客户端应当继续发送请求的剩余部分,或者如果请求已经完成,忽略这个响应.服务器必须在 ...

  3. C++语言中的四种类型转换

    1 引子 这篇笔记是根据StackOverflow上面的一个问题整理而成,主要内容是对C/C++当中四种类型转换操作进行举例说明.在之前其实对它们都是有所了解的,而随着自己在进行总结,并敲了一些测试示 ...

  4. Linux系统——Ansible批量管理工具

    批量管理工具: (1)ansible 操作简单(适用于500台以下服务器) (2)saltstack 比较复杂(一般适用于1000-4w台服务器) (3)puppet超级复杂 systemctl(统一 ...

  5. Liferay portlet首选项(preferences)设置、存储处理

    最近使用Liferay做项目集成,由于团队人员在之前对Liferay这东西都没有充分的了解,用起来着实费了不少力气,最近为了为定制好的portlet添加初始化信息,翻阅了一些文章,下面是了解到的关于初 ...

  6. iOS开发之开发者申请

      一.对于真机调试,首先要在苹果网站上注册APP ID,以及购买iPhone Develop Program(iDP) 开发者授权,99美元.然后要创建证书请求CSR,创建步骤如下: 1.Mac O ...

  7. WdatePicker显示乱码

    1.修改zh-cn.js内容: var $lang={ errAlertMsg: "不合法的日期格式或者日期超出限定范围,需要撤销吗?", aWeekStr: ["周&q ...

  8. 裸眼 3D 技术是什么原理?

    https://www.zhihu.com/question/19553745 作者:杨英东链接:https://www.zhihu.com/question/19553745/answer/1227 ...

  9. RESTful源码学习笔记之RPC和 RESTful 什么区别

    REST,即Representational State Transfer的缩写.翻译过来是表现层状态转换.如果一个架构符合REST原则,就称它为RESTful架构.啥叫json-rpc?接口调用通常 ...

  10. 参考sectools,每个人至少查找5种安全工具、库等信息并深入研究至少两种并写出使用教程

    1.Nessus Nessus是免费网络漏洞扫描器,它可以运行于几乎所有的UNIX平台之上.它不仅能永久升级,还免费提供多达11000种插件(但需要注册并接受EULA-acceptance--终端用户 ...