3.8 线程同步 3.9互斥锁 3.10死锁 3.11读写锁

文章通过示例代码介绍了多线程中出现的线程同步问题,如多个线程同时购买同一张票。为解决此问题,引入了互斥锁来确保同一时间只有一个线程访问共享资源。接着讨论了死锁现象,即两个线程相互等待对方释放资源导致的僵局。最后,提到了读写锁作为提高效率的解决方案,允许多个读线程并发访问,而写线程独占资源。

3.8 线程同步

问题:卖第0张、第-1张门票,三个线程买同一张门票等等。
原因:三个线程并发执行去抢占线程资源,A进来休眠6000微秒的时候,B、C也可能进来。多个线程同时处理一个共享资源,出现线程同步问题,操作必须是原子性的。

/*
    使用多线程实现买票的案例。
    有3个窗口,一共是100张票。
*/

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>

// 全局变量,所有的线程都共享这一份资源。
int tickets = 100;

//回调函数,函数指针
void * sellticket(void * arg) {
    // 卖票
    while(tickets > 0) {
        usleep(6000);//睡眠6000微秒
        printf("%ld 正在卖第 %d 张门票\n", pthread_self(), tickets);
        tickets--;
    }
    return NULL;
}

int main() {

    // 创建3个子线程,子线程做同样的事情,主线程不做买票操作,只做回收子线程资源等
    pthread_t tid1, tid2, tid3;
    pthread_create(&tid1, NULL, sellticket, NULL);
    pthread_create(&tid2, NULL, sellticket, NULL);
    pthread_create(&tid3, NULL, sellticket, NULL);

    // 回收子线程的资源,阻塞,连接
    pthread_join(tid1, NULL);
    pthread_join(tid2, NULL);
    pthread_join(tid3, NULL);

    // 设置线程分离。
    // pthread_detach(tid1);
    // pthread_detach(tid2);
    // pthread_detach(tid3);

    pthread_exit(NULL); // 退出主线程

    return 0;
}

显示错误结果:
在这里插入图片描述

在这里插入图片描述
线程同步会带来一定的效率问题,但是是必要的。

3.9互斥锁

互斥量

在这里插入图片描述
在这里插入图片描述

互斥量相关操作函数

在这里插入图片描述
在这里插入图片描述

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>

// 全局变量,所有的线程都共享这一份资源。
int tickets = 100;

// 创建一个互斥量
pthread_mutex_t mutex;

void * sellticket(void * arg) {

    // 卖票
    while(1) {

        // 加锁
        pthread_mutex_lock(&mutex);

        if(tickets > 0) {
            usleep(6000);
            printf("%ld 正在卖第 %d 张门票\n", pthread_self(), tickets);
            tickets--;
        }else {
            // 解锁
            pthread_mutex_unlock(&mutex);
            break;
        }

        // 解锁
        pthread_mutex_unlock(&mutex);
    }

    

    return NULL;
}

int main() {

    // 初始化互斥量
    pthread_mutex_init(&mutex, NULL);

    // 创建3个子线程
    pthread_t tid1, tid2, tid3;
    pthread_create(&tid1, NULL, sellticket, NULL);
    pthread_create(&tid2, NULL, sellticket, NULL);
    pthread_create(&tid3, NULL, sellticket, NULL);

    // 回收子线程的资源,阻塞
    pthread_join(tid1, NULL);
    pthread_join(tid2, NULL);
    pthread_join(tid3, NULL);

    pthread_exit(NULL); // 退出主线程

    // 释放互斥量资源
    pthread_mutex_destroy(&mutex);

    return 0;
}

在这里插入图片描述

3.10死锁

在这里插入图片描述

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>

// 创建2个互斥量
pthread_mutex_t mutex1, mutex2;

void * workA(void * arg) {

    pthread_mutex_lock(&mutex1);
    sleep(1);
    pthread_mutex_lock(&mutex2);

    printf("workA....\n");

	//先解2锁再解1锁
    pthread_mutex_unlock(&mutex2);
    pthread_mutex_unlock(&mutex1);
    return NULL;
}


void * workB(void * arg) {
    pthread_mutex_lock(&mutex2);
    sleep(1);
    pthread_mutex_lock(&mutex1);

    printf("workB....\n");

    pthread_mutex_unlock(&mutex1);
    pthread_mutex_unlock(&mutex2);

    return NULL;
}

int main() {

    // 初始化互斥量
    pthread_mutex_init(&mutex1, NULL);
    pthread_mutex_init(&mutex2, NULL);

    // 创建2个子线程
    pthread_t tid1, tid2;
    pthread_create(&tid1, NULL, workA, NULL);
    pthread_create(&tid2, NULL, workB, NULL);

    // 回收子线程资源
    pthread_join(tid1, NULL);
    pthread_join(tid2, NULL);

    // 释放互斥量资源
    pthread_mutex_destroy(&mutex1);
    pthread_mutex_destroy(&mutex2);

    return 0;
}

上述代码产生死锁:线程1获得锁1且申请锁2,线程2获得锁2且申请锁一。
产生死锁的四个必要条件:
互斥条件、占有且等待条件、不可抢占条件、循环等待条件。

3.11读写锁

读写锁

读写锁比互斥锁效率要高一点,读的时候是并发执行,而互斥锁是串行。

在这里插入图片描述

读写锁相关操作函数

在这里插入图片描述
在这里插入图片描述
代码:

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>

// 创建一个共享数据
int num = 1;
// pthread_mutex_t mutex;
pthread_rwlock_t rwlock;

void * writeNum(void * arg) {

    while(1) {
        pthread_rwlock_wrlock(&rwlock);
        num++;
        printf("++write, tid : %ld, num : %d\n", pthread_self(), num);
        pthread_rwlock_unlock(&rwlock);
        usleep(100);
    }

    return NULL;
}

void * readNum(void * arg) {

    while(1) {
        pthread_rwlock_rdlock(&rwlock);
        printf("===read, tid : %ld, num : %d\n", pthread_self(), num);
        pthread_rwlock_unlock(&rwlock);
        usleep(100);
    }

    return NULL;
}

int main() {

   pthread_rwlock_init(&rwlock, NULL);

    // 创建3个写线程,5个读线程
    pthread_t wtids[3], rtids[5];
    for(int i = 0; i < 3; i++) {
        pthread_create(&wtids[i], NULL, writeNum, NULL);
    }

    for(int i = 0; i < 5; i++) {
        pthread_create(&rtids[i], NULL, readNum, NULL);
    }

    // 设置线程分离
    for(int i = 0; i < 3; i++) {
       pthread_detach(wtids[i]);
    }

    for(int i = 0; i < 5; i++) {
         pthread_detach(rtids[i]);
    }

	//若不加该语句,return 0的话,主线程的退出会影响子线程的执行
	//加上该语句后,主线程的退出不影响子线程的执行
    pthread_exit(NULL);

    pthread_rwlock_destroy(&rwlock);

    return 0;
}

显示结果:
在这里插入图片描述

凌云面试自查(操作系统) 1.Linux基础 1.1Linux 如何查看磁盘分区大小, 如何查看、修改网卡配 置 1.2如何修改网卡的IP、子网掩码、默认网关、DNS 1.3查看当前操作系统内核信息 1.4当前操作系统版本信息 1.5查看内存信息 1.6查看进程占用的文件 1.7查看进程占用的地址空间 1.8查看cpu,以及系统硬件信息 1.9查找所有.c文件中含有“hello world”的文件 1.10操作系统五大功能 1.11进程调度有哪些调度算法 1.12内存管理有哪些调度算法 1.13什么是软链接, 什么是硬链接 1.14什么是可重入函数, 不可重入函数? 哪些原因导致函数 不可重入? 1.15.gdb是什么? 怎么使用的? 1.16.linux下有哪些设备? 1.17.什么是VFS(虚拟文件系统)? 1.18.什么是块设备和字符设备和网络设备? 2.APUE 2.1.什么是文件IO, IO常用的系统调用有哪些? 如何实现 文件拷贝? 2.2.系统调用(系统API, 文件IO)和标准IO(库函数)有什么区 别? 什么是缓冲、缓冲有哪些 2.3.么是同步, 异步? 什么是阻塞和非阻塞? 2.4. 怎么将socket设置为阻塞/非阻塞模式? 2.5.说一下五种I/O模型? (不是select, poll, epoll) 2.6. 描述select, poll, epoll的工作原理及区别? 2.7.什么是内核态和用户态? 内核到用户的转换 3.多进程与多线程 3.1. 进程和线程的概念 3.2. 线程和进程有什么区别 3.3. 什么时候使用多进程, 什么时候使用多线程 3.4.进程三态与五态是什么? 3.5. 创建进程有哪些方式(了解即可) 3.6.什么是进程上下文与中断上下文? 3.7.fork和vfork有什么区别? 为什么又fork还要vfork? 3.8.什么是写时拷贝技术? 3.9.什么是孤儿进程, 什么是僵尸进程, 如何避免产生僵 尸进程? 3.10.如何知道进程和线程是否已经退出? 3.11.一个系统最大可以启动多少个线程? 怎么设置线程的 优先级? 3.12.线程为什么要设置属性, 为什么设置栈的大小? 3.13.线程的堆栈大小要怎么设置, 默认是多少, 最小是多少? 3.14.怎么保证主线程创建的子线程先执行? 线程间通信有 哪些方式? 3.15.什么是线程同步与互斥? 互斥锁, 条件变量和信号量 的区别? 3.16.什么是临界区, 什么是临界资源? 3.17.什么是线程池? 3.18. 进程间通信有几种? 3.19.什么是死锁? 死锁产生的原因? 死锁的产生有哪些条 件? 怎么解决死锁? 3.20.线程间怎么进行通信以及同步 3.21.线程同步实现的方式? 3.22.Linux 内核中有哪几种锁; 自旋锁与互斥锁有什么区 别? 3.23.条件变量和信号量的区别 3.24.条件变量和互斥锁为什么要配合使用 3.25信号与信号量的区别 4.BSP 4.1描述一下字符设备驱动编写的流程 4.2什么是中断的上半部, 什么是中断的下半部? 4.3.中断的下半部有哪些实现机制? 4.4.Linux 系统启动流程及U-boot
最新发布
09-11
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值