静态方式:(一般不用)
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_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;
}
|
find /usr/include/ -name *.h | xargs grep pthread_mutexattr_t
因为是 typedef 出来的,所以看到大括号就是我们要找的,直接 vim 绝对路径 查看:
加锁注意事项
如果线程在加锁后解锁前被取消,锁将永远保持锁定状态,因此如果在关键区段内有取消点存在,则必须在退出回调函数 pthread_cleanup_push/pthread_cleanup_pop 中解锁。同时不应该在信号处理函数中使用互斥锁,否则容易造成死锁。