一、互斥量概念与使用
  • 概念:可以使用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)一下,如果人多就不排队(不阻塞)直接退出

五、演示案例

APUE编程:57---线程处理(互斥量:pthread_mutex_t)_互斥量的定义

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

APUE编程:57---线程处理(互斥量:pthread_mutex_t)_互斥量的加锁_02

演示案例:下面给出了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);
}

运行结果

APUE编程:57---线程处理(互斥量:pthread_mutex_t)_互斥量的定义_03

  • 这个程序故意对它已有的互斥量进行加锁,目的是演示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);
        }
    }
}

 APUE编程:57---线程处理(互斥量:pthread_mutex_t)_互斥量的加锁_04

八、演示案例
  • 两个子线程对主线程中的数字进行读取了递减,数字变为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;
    }
}

APUE编程:57---线程处理(互斥量:pthread_mutex_t)_互斥量的定义_05