Linux多线程4-1_互斥量

 ̄綄美尐妖づ 提交于 2019-12-26 23:41:15

一、为什么要使用互斥量
1、当多个线程共享相同的内存时,需要每一个线程看到相同的视图。当一个线程修改变量时,而其他线程也可以读取或者修改这个变量,就需要对
这些线程同步,确保他们不会访问到无效的变量
2、在变量修改时间多于一个存储器访问周期的处理器结构中,当存储器的读和写这两个周期交叉时,这种潜在的不一致性就会出现。当然这与处理
器相关,但是在可移植的程序中并不能对处理器做出任何假设

二、互斥量的初始化
1、为了让线程访问数据不产生冲突,这要就需要对变量加锁,使得同一时刻只有一个线程可以访问变量。互斥量本质就是锁,访问共享资源前对
互斥量加锁,访问完成后解锁
2、当互斥量加锁以后,其他所有需要访问该互斥量的线程都将阻塞
3、当互斥量解锁以后,所有因为这个互斥量阻塞的线程都将变为就绪态,第一个获得cpu的线程会获得互斥量,变为运行态,而其他线程会继续
变为阻塞,在这种方式下访问互斥量每次只有一个线程能向前执行
4、互斥量用pthread_mutex_t类型的数据表示,在使用之前需要对互斥量初始化
1)、如果是动态分配的互斥量,可以调用pthread_mutex_init()函数初始化
2)、如果是静态分配的互斥量,还可以把它置为常量PTHREAD_MUTEX_INITIALIZER
3)、动态分配的互斥量在释放内存之前需要调用pthread_mutex_destroy()

​ int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);
​ int pthread_mutex_destroy(pthread_mutex_t *mutex);
​ pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

三、加锁和解锁
加锁
int pthread_mutex_lock(pthread_mutex_t *mutex);
成功返回0,失败返回错误码。如果互斥量已经被锁住,那么会导致该线程阻塞
int pthread_mutex_trylock(pthread_mutex_t *mutex);
成功返回0,失败返回错误码。如果互斥量已经被锁住,不会导致线程阻塞

解锁
int pthread_mutex_unlock(pthread_mutex_t *mutex);
成功返回0,失败返回错误码。如果一个互斥量没有被锁住,那么解锁就会出错

四、死锁
1、死锁:线程一直在等待锁,而锁却无法解开
2、如果一个线程对已经占有的互斥量继续加锁,那么他就会陷入死锁状态。
lock mutex--------lock mutex--------阻塞
解除阻塞状态的条件:mutex unlock
mutex unlock的条件: 解除阻塞态
3、程序中使用多个互斥量时,如果一个线程一直占有互斥量A,并且试图加锁互斥量B,但是拥有互斥量B的线程却要加锁互斥量A,这时就会出现死锁
线程1: lock A成功
线程2 : lock B成功
线程1: lock B失败, 阻塞
线程2: lock A失败, 阻塞
线程1解除阻塞的条件:线程2将B解锁
线程2解除阻塞的条件:线程1将A解锁
4、如何去避免
你可以小心的控制互斥量加锁的顺序来避免死锁,例如所有的线程都在加锁B之前先加锁A,那么这两个互斥量就不会产生死锁了。有的时候
程序写的多了互斥量就难以把控,你可以先释放已经占有的锁,然后再加锁其他互斥量。
5、互斥量使用要注意:
1)、访问共享资源时需要加锁
2)、互斥量使用完之后需要销毁
3)、加锁之后一定要解锁
4)、互斥量加锁的范围要小
5)、互斥量的数量应该少

六、互斥量实例
1、程序框图

2、源代码

/*
 *DESCRIPTION:    多线程访问变量产生错误的例子
 */

#include "apue.h"

struct student{
    int id;
    int age;
    int name;
}stu;

//定义两个全局变量,因为两个线程都要访问
int i;
pthread_mutex_t mutex;

void *thread_fun1(void *arg)
{
    while(1)
    {
        //加锁,对整个结构体访问进行加锁,防止产生错乱
        pthread_mutex_lock(&mutex);
        stu.id = i;
        stu.age = i;
        stu.name = i;
        i++;
        if(stu.id !=stu.age || stu.id !=stu.name || stu.age!=stu.name)
        {
            printf("thread 1 %d,%d,%d\n", stu.id, stu.age, stu.name);
            break;
        }
        //访问变量完成,需要进行解锁,只有这样其他线程才能访问
        pthread_mutex_unlock(&mutex);
    }

    return(void *)0;
}

void *thread_fun2(void *arg)
{
    while(1)
    {
        //加锁,对整个结构体访问进行加锁,防止产生错乱
        pthread_mutex_lock(&mutex);
        stu.id = i;
        stu.age = i;
        stu.name = i;
        i++;
        if(stu.id!=stu.age || stu.id!=stu.name || stu.age!=stu.name)
        {
            printf("thread 2 %d,%d,%d\n", stu.id, stu.age, stu.name);
            break;
        }
        pthread_mutex_unlock(&mutex);
    }

    return(void *)0;
}

int main()
{
    pthread_t tid1, tid2;
    int err;
    //对互斥量进行初始化,只有初始化过到互斥量才能使用
    err = pthread_mutex_init(&mutex, NULL);
    if(err != 0)
    {
        printf("init mutex failed\n");
        return;
    }
    //床在新线程
    err = pthread_create(&tid1, NULL, thread_fun1, NULL);
    if(err != 0)
    {
        printf("create new thread failed\n");
        return;
    }
    //创造新线程
    err = pthread_create(&tid2, NULL, thread_fun2, NULL);
    if(err != 0)
    {
        printf("create new thread failed\n");
        return;
    }
    //等待新线程运行结束
    pthread_join(tid1, NULL);
    pthread_join(tid2, NULL);

    return 0;
}

作业:使用多线程对一个队列进行增加和减少,增加操作是一个线程,删除操作是一个线程

/*
 *DESCRIPTION:    将文件1.c 拷贝到11.c 12.c 13.c
 *    
 */
#include "apue.h"

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
struct queue{
    int len;
    int write_pos;
    int read_pos;
    int data[50];
};

struct queue *queue_init()
{
    struct queue *que;
    //申请内存
    que = (struct queue *)malloc(sizeof(struct queue));
    if(que ==NULL)
    {
        printf("malloc failed\n");
        return;
    }

    //初始化
    que->len = 0;
    que->write_pos = 0;
    que->read_pos = 0;

    return que;
}

void queue_destroy(struct queue *que)
{
    //销毁互斥量和que
    pthread_mutex_destroy(&mutex);
    free(que);
}

void *queue_add(void *arg)
{
    struct queue *que = (struct queue *)arg;
    int buf=0;
    while(buf<50)
    {
        pthread_mutex_lock(&mutex);
        que->data[que->write_pos] = buf;
        que->write_pos ++;
        que->len ++;
        buf++;
        printf("write data %d to queue\n", que->data[que->write_pos -1]);

        pthread_mutex_unlock(&mutex);
        sleep(1);
    }
}

void *queue_del(void *arg)
{
    struct queue *que = (struct queue *)arg;
    int buf=0;
    while(1)
    {
        sleep(2);
        pthread_mutex_lock(&mutex);
        buf = que->data[que->read_pos];
        que->read_pos ++;
        if(que->len -- == 0)
        {
            printf("queue is empty\n");
            return;
        }
        buf++;
        printf("read data %d from queue\n", que->data[que->read_pos -1]);
        pthread_mutex_unlock(&mutex);
    }
}

int main()
{
    pthread_t tid1, tid2;
    int err;
    struct queue *que;

    //队列和锁都要初始化
    que = queue_init();
    err = pthread_mutex_init(&mutex, NULL);
    if(err)
    {
        printf("mutex init failed\n");
        free(que);
        return;
    }

    err = pthread_create(&tid1, NULL, queue_add, (void *)que);
    if(err)
    {
        printf("create add thread failed\n");
        queue_destroy(que);
        return;
    }

    err = pthread_create(&tid2, NULL, queue_del, (void *)que);
    if(err)
    {
        printf("create del thread failed\n");
        queue_destroy(que);
        return;
    }

    //等待增加和删除操作完成
    pthread_join(tid1, NULL);
    pthread_join(tid2, NULL);

    //销毁
    queue_destroy(que);
}

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!