【Linux系统编程】互斥量(互斥锁)

  互斥锁

        线程的主要优势在于能够通过全局变量来共享信息。这种便捷的共享是有代价的:必须确保多个线程不会同时修改同一变量,或者某一线程不会读取正由其他线程修改的变量。
        互斥量可以保护对共享变量的访问。

a++需要执行3个步骤:

1.取a的值

2.计算a+1

3.a+1赋值给a

        某一时刻,全局变量a的值为5,线程A和线程B中都要执行a++,如果线程A在执行a+1赋值给a之前,这时线程B执行了a++,线程B取到a的值仍是5,这时,线程A和B赋给a的值都是6。值为5的变量a经过两次a++结果却是6,显然出现了错误。 ​

        多线程竞争操作共享变量的这段代码叫做临界区。多个进程同时操作临界区会产生错误所以这段代码应该互斥,当一个线程执行临界区时,应该阻止其他线程进入临界区。 ​

        为避免线程更新共享变量时出现问题,可以使用互斥量(mutex是mutual exclusion的缩写)来确保同时仅有一个线程可以访问某项共享资源。 ​

        互斥量的作用类似于一个“锁”,当一个线程访问共享资源时,它必须先尝试获取互斥量的锁。如果互斥量当前没有被其他线程占用,那么该线程将成功获取锁,并可以安全地访问共享资源;如果互斥量已经被其他线程占用,那么该线程将被阻塞,直到互斥量的锁被释放。 ​

        互斥量有两种状态:已锁定(locked)和未锁定(unlocked)。至多只有一个线程可以锁定该互斥量,试图对已经锁定的某一互斥量再次加锁将会阻塞线程。一旦线程锁定互斥量,随即成为该互斥量的所有者,只有所有者才能给互斥量解锁。

pthread_mutex_init 函数

函数描述:

​    初始化一个互斥量

函数原型:

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


函数参数:

​    ●mutex:指向互斥量的指针,下面几个函数都有该参数,不一一介绍

​    ●mutexattr:指向定义互斥量属性的指针,取默认值传NULL

函数返回值:

​    ●成功返回0

​    ●失败返回错误号

pthread_mutex_lock和pthread_mutex_unlock函数

函数描述:

​        给互斥量加锁和解锁,解锁的函数在解锁的同时唤醒阻塞在该互斥量上的线程,默认先阻塞的先唤醒。

函数原型:

int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);


函数返回值:

​    ●成功返回0

​    ●出现错误返回错误号。加锁不成功,线程阻塞

pthread_mutex_destroy函数

函数描述:

​    销毁一个互斥量

函数原型:

int pthread_mutex_destroy(pthread_mutex_t *mutex);


函数返回值:

​    ●成功返回0

​    ●失败返回错误号

对这段代码加上相同的锁。

#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>
#include<fcntl.h>
#include<stdlib.h>

#define N 10000
int a = 0;
pthread_mutex_t mtx;

void* thread1(void* arg) {
    for(int i = 0; i < N; i++){
        pthread_mutex_lock(&mtx);
        a++;
        pthread_mutex_unlock(&mtx);
    }
    return NULL;
}

int main(int argc, char* argv[])
{
    pthread_mutex_init(&mtx, NULL);
    pthread_t tid;
    pthread_create(&tid, NULL, thread1, NULL);

    for(int i = 0; i < N; i++){
        pthread_mutex_lock(&mtx);
        a++;
        pthread_mutex_unlock(&mtx);
    }
    pthread_join(tid, NULL);
    printf("a = %d\n", a);
    
    pthread_mutex_destroy(&mtx);
    return 0;
}

pthread_mutex_trylock函数

函数描述:

​    尝试给互斥量加锁,加锁不成功直接返回错误号(EBUSY),不会阻塞,其他与pthread_mutex_lock相同。

#define N 10000
int a = 0;
pthread_mutex_t mtx;

void* thread1(void* arg) {
    sleep(1);
    int ret = pthread_mutex_trylock(&mtx);
    if(ret != 0 && ret == EBUSY){
        printf("非阻塞加锁, 当前锁被占用\n");
    }
}

int main(int argc, char* argv[])
{
    pthread_mutex_init(&mtx, NULL);
    pthread_t tid;
    pthread_create(&tid, NULL, thread1, NULL);
    pthread_mutex_lock(&mtx);
    pthread_exit(NULL);
}

练习

        模拟选座系统,有10个空座,12个用户(线程)同时选座,系统从空座中随机选择一个,打印出当前用户的序号和选中座位序号(线程创建顺序就是用户序号顺序)。
输出正确结果应为:10个用户成功,2个失败,没有重复座位

#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>
#include<fcntl.h>
#include<errno.h>
#include<stdlib.h>
#include<time.h>

pthread_mutex_t mtx;

int empty_seats_num = 10;//初始空座10个
int empty_seats[10];

//每次得到新的随机数
int choose_seat() {
    if(empty_seats_num == 0){
        return -1;
    }
    int seat_insex = rand() % empty_seats_num;//空位下标
    int seat_number = empty_seats[seat_insex];//根据下标把座位取出来
    
    //把最后的10放进来
    empty_seats[seat_insex] = empty_seats[empty_seats_num - 1];
    empty_seats_num--;
    
    return seat_number;//得到座位数
}

void* thread1(void* arg) {
    pthread_mutex_lock(&mtx);//加锁
    int seat_number = choose_seat();
    pthread_mutex_unlock(&mtx);//解锁
    if(seat_number != -1) {
        printf("用户 %d 成功选得座位数为 %d\n", *(int*)arg, seat_number);
    } else {
        printf("用户 %d 选座失败,座位已售空\n", *(int*)arg);
    }
}

int main(int argc, char* argv[])
{
    srand(time(NULL));
    for (int i = 0; i < 10; i++){
        empty_seats[i] = i+1;//循环遍历输入十个数
    }
    pthread_mutex_init(&mtx, NULL);//初始化一个互斥量

    //创建12个线程的线程id的数组
    pthread_t tids[12];
    for (int i = 0; i < 12; i++){
        int* user_number = (int*)malloc(sizeof(int));
        * user_number = i + 1;
        pthread_create(&tids[i], NULL, thread1, user_number);
    }
    //等待指定线程终止并回收
    for (int i = 0; i < 12; i++){
        pthread_join(tids[i], NULL);
    }
    //销毁
    pthread_mutex_destroy(&mtx);
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值