- 概念:可以使用pthread的互斥接口来保护数据,确保同一时间只有一个线程访问数据
二、互斥变量(pthread_mutex_t)互斥量的使用:
- 互斥量从本质上说是一把锁,在访问共享资源前对互斥量进行设置(加锁),在访问完成后释放(解锁)互斥量
- 对互斥量进行加锁以后,任何其他试图再次对互斥量加锁的线程都会被阻塞,直到当前线程释放该互斥锁
- 如果释放互斥量时有一个以上的线程阻塞,那么所有该锁上的阻塞线程都会变成可运行状态,第一个变为运行的线程就可以对互斥量加锁,其他线程就会看到互斥量依然是锁着的,只能再次等待它重新变为可用(在这种方式下,每次只有一个线程可以向前执行)
- 互斥量数据类型:pthread_mutex_t
①静态初始化
- 直接把pthread_mutex_t互斥变量设置为常量PTHREAD_MUTEX_INITIALIZER
- 静态初始化互斥变量只能拥有默认的互斥量属性,不能设置其他互斥量属性(互斥量属性见文章:javascript:void(0))
- 例如:
pthread_mutex_t mutex; mutex=PTHREAD_MUTEX_INITIALIZER; //或者 pthread_mutex_t *mutex=(pthread_mutex_t *)malloc(sizeof(pthread_mutex_t)); *mutex=PTHREAD_MUTEX_INITIALIZER;
四、互斥量的加锁与解锁②动态初始化
- 静态初始化互斥变量只能拥有默认互斥量属性,我们可以通过pthread_mutex_init函数来动态初始化互斥量,并且可以在初始化时选择设置互斥量的属性
#include <pthread.h> int pthread_mutex_init(pthread_mutex_t *restrict mutex,const pthread_mutexattr_t *restrict attr); int pthread_mutex_destroy(pthread_mutex_t *mutex); // 返回值:成功返回0,否则返回错误编号
pthread_mutex_init:
- 功能:对互斥量进行初始化
- 参数:
- 参数1: 需要初始化互斥量
- 参数2:初始化时互斥量的属性。如果使用默认属性,此处填NULL(互斥量属性见文章:javascript:void(0))
pthread_mutex_destroy:
- 功能:互斥量的反初始化
- 参数:互斥量
- 备注(重点):此函数只是反初始化互斥量,并没有释放内存空间,如果互斥量是通过malloc等函数申请的,那么需要在free掉互斥量之前调用pthread_mutex_destroy函数
例如:
pthread_mutex_t mutex; pthread_mutex_init(&mutex,NULL); /*do something*/ pthread_mutex_destroy(&mutex);
pthread_mutex_t* mutex=(pthread_mutex_t*)malloc(sizeof(pthread_mutex_t)); pthread_mutex_init(mutex,NULL); /*do something*/ pthread_mutex_destroy(mutex); free(mutex);
#include <pthread.h>
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
// 返回值:成功返回0,否则返回错误编号
五、演示案例
- pthread_mutex_lock:对互斥量进行加锁。如果互斥量已经上锁,调用线程将阻塞到互斥量被解锁
- pthread_mutex_unlock:对互斥量进行解锁
- pthead_mutex_trylock:对互斥量进行尝试加锁(非阻塞)。如果互斥量处于未加锁状态,那么pthead_mutex_trylock就会锁住这个互斥量;如果此锁处于加锁状态,那么pthead_mutex_trylock就出错返回EBUSY,并且不会阻塞
例如去食堂吃饭:如果食堂人太多(加锁),pthread_mutex_lock就会排队等待(阻塞);pthead_mutex_trylock是去食堂尝试(try)一下,如果人多就不排队(不阻塞)直接退出
struct foo {
int f_count;
pthread_mutex_t f_lock;
int f_id;
};
struct foo *foo_alloc(int id)
{
struct foo *fp;
if ((fp == malloc(sizeof(struct foo))) != NULL) {
fp->f_count = 1;
fp->f_id = id;
if (pthread_mutex_init(&fp->f_lock, NULL) != 0) {
free(fp);
fp = NULL;
}
}
return fp;
}
void foo_hold(struct foo *fp)
{
pthread_mutex_lock(&fp->f_lock);
fp->f_count++;
pthread_mutex_unlock(&fp->f_lock);
}
void foo_rele(struct foo *fp)
{
pthread_mutex_lock(&fp->f_lock);
if (--fp->f_count == 0) {
pthread_mutex_unlock(&fp->f_lock);
pthread_mutex_destory(&fp->f_lock);
free(fp);
} else {
pthread_mutex_unlock(&fp->f_lock);
}
}
六、超时互斥锁(pthread_mutex_timedlock)
#include <pthread.h>
#include <time.h>
int pthread_mutex_timedlock(pthread_mutex_t *restrict mutex,const struct timespec *restrict tsptr);
// 返回值:成功返回0,否则返回错误编号.
- 功能:当线程试图获取一个已加锁的互斥量时,pthread_mutex_timedlock允许绑定线程阻塞时间
-
参数:
- mutex:尝试要加锁的互斥量
- tsptr:设置的超时时间
- 特点:在达到超时时间值时,如果还不能对互斥量成功加锁,那么就返回错误码ETIMEDOUT
- 超时时间的注意:超时指定愿意等待的绝对时间。这个时间值是一个绝对数而不是相对数。例如,假设愿意等待3分钟,不是把3分钟转换为timespec结构体传递给参数2,而是需要把当前时间加上3分钟再转换成timespec结构然后传递给参数2
七、演示案例演示案例:下面给出了pthread_mutex_timedlock避免永久阻塞
#include <pthread.h> int main(void) { int err; struct timespec tout; struct tm *tmp; char buf[64]; pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; pthread_mutex_lock(&lock); printf("mutex is locked\n"); clock_gettime(CLOCK_REALTIME, &tout); tmp = localtime(&tout.tv_sec); strftime(buf, sizeof(buf), "%r", tmp); printf("current time is %s\n", buf); tout.tv_sec += 10; /* 10 seconds from now */ /* caution: this could lead to deadlock */ err = pthread_mutex_timedlock(&lock, &tout); clock_gettime(CLOCK_REALTIME, &tout); tmp = localtime(&tout.tv_sec); strftime(buf, sizeof(buf), "%r", tmp); printf("the time is now %s\n", buf); if (err == 0) printf("mutex locked again!\n"); else printf("can’t lock mutex again: %s\n", strerror(err)); exit(0); }
运行结果
- 这个程序故意对它已有的互斥量进行加锁,目的是演示pthread_mutex_timedlock是如何工作的。不推荐在实际中使用这种方法,因为会导致死锁
- 注意:阻塞的时间可能会有所不同,造成不同的原因有多种:开始时间可能在某秒的中间位置,系统时钟的精度可能不足以精确到支持我们指定的超时时间值,或者在程序继续运行前,调度延迟可能会增加时间值
- 主线程写,子线程读
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#define MAXSIZE 1024
void *func(void *arg);
char buff[MAXSIZE];
pthread_mutex_t mutex;
int main()
{
void* exit_value;
pthread_t tid;
//create thread
if(pthread_create(&tid,NULL,func,NULL)!=0){
perror("pthread_create");
exit(EXIT_FAILURE);
}
//init mutex
if(pthread_mutex_init(&mutex,NULL)!=0){
perror("pthread_mutex_init");
exit(EXIT_FAILURE);
}
while(1)
{
pthread_mutex_lock(&mutex); //lock
printf("Enter(end to quit):");
fflush(stdout);
scanf("%s",buff);
if(strncmp(buff,"end",3)==0){
pthread_mutex_unlock(&mutex); //unlock
break;
}
pthread_mutex_unlock(&mutex); //unlock
sleep(1);
}
//等待进程结束
printf("join......\n");
if(pthread_join(tid,&exit_value)!=0){
perror("pthread_join");
exit(EXIT_FAILURE);
}
//destory mutex
pthread_mutex_destroy(&mutex);
exit(0);
}
void *func(void *arg)
{
sleep(1);
while(1)
{
pthread_mutex_lock(&mutex);//lock
if(strlen(buff)!=0){
if(strncmp(buff,"end",3)==0){
pthread_mutex_unlock(&mutex);//unlock
pthread_exit(0);
}
printf("read %d bytes\n",strlen(buff));
buff[0]='\0';
pthread_mutex_unlock(&mutex); //unlock
sleep(1);
}
}
}
八、演示案例
- 两个子线程对主线程中的数字进行读取了递减,数字变为0之后,两个子进程都结束
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
pthread_mutex_t *mutex;
int success=0;
int error=1;
void *func(void *arg);
void free_mutex(pthread_mutex_t *mutex);
int main()
{
int num=10;
int *retValue=(int*)malloc(sizeof(int));
pthread_t tid1,tid2;
mutex=(pthread_mutex_t*)malloc(sizeof(pthread_mutex_t));
if(pthread_mutex_init(mutex,NULL)!=0){
perror("pthread_mutex_init error");
free_mutex(mutex);
exit(EXIT_FAILURE);
}
if(pthread_create(&tid1,NULL,func,(void*)&num)!=0){
perror("thread 1 create error");
free_mutex(mutex);
exit(EXIT_FAILURE);
}
if(pthread_create(&tid2,NULL,func,(void*)&num)!=0){
perror("thread 2 create error");
free_mutex(mutex);
exit(EXIT_FAILURE);
}
if(pthread_join(tid1,(void**)&retValue)!=0){
perror("pthread_join 1 error");
free_mutex(mutex);
exit(EXIT_FAILURE);
}else{
if((*retValue)==0){
printf("Thread1 return success\n");
}else{
printf("Thread1 return error\n");
}
}
if(pthread_join(tid2,(void**)&retValue)!=0){
perror("pthread_join 2 error");
free_mutex(mutex);
exit(EXIT_FAILURE);
}else{
if((*retValue)==0){
printf("Thread2 return success\n");
}else{
printf("Thread2 return error\n");
}
}
if(pthread_mutex_destroy(mutex)!=0){
perror("pthread_mutex_destroy error");
exit(EXIT_FAILURE);
}
free_mutex(mutex);
//pthread_join会自动释放这个内存,如果此处还释放retValue,程序就会出错
/*if(retValue){
free(retValue);
retValue=NULL;
}*/
exit(EXIT_SUCCESS);
}
void *func(void *arg)
{
int *num=(int*)(arg);
while(1)
{
if(pthread_mutex_lock(mutex)!=0){
printf("Thread %lu:pthread_mutex_destroy error",pthread_self());
pthread_exit(&error);
}
if((*num)<=0){
if(pthread_mutex_unlock(mutex)!=0){
printf("Thread %lu:pthread_mutex_unlock error",pthread_self());
pthread_exit(&error);
}
break;
}
printf("Thread %lu:current num is %d\n",pthread_self(),*num);
(*num)--;
if(pthread_mutex_unlock(mutex)!=0){
printf("Thread %lu:pthread_mutex_unlock error",pthread_self());
pthread_exit(&error);
}
sleep(1);
}
pthread_exit(&success);
}
void free_mutex(pthread_mutex_t *mutex)
{
if(mutex!=NULL){
free(mutex);
mutex=NULL;
}
}