1. 前言
如果你不了解线程的基本概念,请你先
移步上一篇文章: 线程基本概念
本章重点:
本篇文章着重讲解线程互斥以及线程
同步的相关概念,以及如何实现它们.
周边概念包括临界资源,原子性,互斥量
等也会在本文当中提及
2. 多线程互斥相关背景概念
在学习互斥前,需要先补充一些相关概念:
临界资源:
多线程执行流共享的资源就叫做临界资源临界区:
每个线程内部,访问有临界资源的代码,就叫做临界区互斥:
任何时刻,互斥保证有且只有一个执行流进入临界区,访问临界资源,通常对临界资源起保护作用原子性:
不会被任何调度机制打断的操作,该操作只有两态,要么完成,要么未完成
。大部分情况,线程使用的数据都是局部变量,变量的地址空间在线程栈空间内,这种情况,变量归属单个线程,其他线程无法获得这种变量。但有时候,很多变量都需要在线程间共享,这样的变量称为共享变量,可以通过数据的共享,完成线程之间的交互,多个线程并发的操作共享变量,会带来一些问题
比如说我们最常见的高铁售票系统,可以把买票操作分为三步: 第一步: 判断现在还有无车票.第二步: 乘客付款后,钱包金额减少. 第三步: 乘客获得一张车票,高铁的总票数减一.多个执行流执行这三步时可能会出现问题,如下图:
可以写一段代码来验证上面的情况:
// 操作共享变量会有问题的售票系统代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
int ticket = 100;
void \*route(void \*arg)
{
char \*id = (char\*)arg;
while ( 1 ) {
if ( ticket > 0 ) {
usleep(1000);
printf("%s sells ticket:%d\n", id, ticket);
ticket--;
} else {
break;
}
}
}
int main( void )
{
pthread_t t1, t2, t3, t4;
pthread\_create(&t1, NULL, route, "thread 1");
pthread\_create(&t2, NULL, route, "thread 2");
pthread\_create(&t3, NULL, route, "thread 3");
pthread\_create(&t4, NULL, route, "thread 4");
//等待线程结束
pthread\_join(t1, NULL);
pthread\_join(t2, NULL);
pthread\_join(t3, NULL);
pthread\_join(t4, NULL);
}
发现多次执行这段代码得到的结果可能不同
为什么会出现不同的结果?
3. 多线程互斥详解
为啥上面可能会出现多种结果?
是有多种原因在里面的:
- if 语句判断条件为真以后,代码可以并发的切换到其他线程
- usleep这个模拟漫长业务的过程,在这个漫长的业务过程中,可能有很多个线程会进入该代码段
- - -ticket操作本身就不是一个原子操作
这里有一个问题,为什么- -ticket操作不是原子的?其实我们鉴定一个操作是不是原子性的可以查看这个操作的汇编代码,若汇编代码只有一条,则我们认为这个操作是原子的,反之则这个操作不是原子性的,可以来看看减减的汇编代码是有三条:
要解决上面的问题,需要满足以下条件:
- 代码必须要有互斥行为:当代码进入临界区执行时,不允许其他线程进入该临界区。
- 如果多个线程同时要求执行临界区的代码,并且临界区没有线程在执行,那么只能允许一个线程进入该临界区。
- 如果线程不在临界区中执行,那么该线程不能阻止其他线程进入临界区。
本质上就是需要一把锁, 互斥锁
任何一个时间,只允许一个线程获得这把锁并且继续向后执行,没拿到锁的线程默认只能在加锁处阻塞等待其他线程释放掉锁才能继续往后走,多个线程来竞争一把锁,它们的关系就是互斥
4. 互斥锁的接口使用
互斥锁的使用一般分为四个步骤:
- 初始化互斥锁
- 在到达临界区前加锁
- 在跑完临界区代码后解锁
- 用完互斥锁后进行销毁
第一步: 初始化互斥锁
方法一, 静态分配
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER
方法二, 动态分配
第二步和第三步: 加解锁
调用pthread_ lock 时,可能会遇到以下情况:
- 互斥量处于未锁状态,该函数会将互斥量锁定,同时返回成功
- 发起函数调用时,其他线程已经锁定互斥量,或者存在其他线程同时申请互斥量,但没有竞争到互斥量,
那么pthread_ lock调用会陷入阻塞(执行流被挂起),等待互斥量解锁
第四步: 销毁互斥锁
所以现在可以更改一下售票系统:
int ticket = 100;
pthread\_mutex\_t mutex;//全局
void \*route(void \*arg)
{
char \*id = (char\*)arg;
while ( 1 ) {
pthread\_mutex\_lock(&mutex);
if ( ticket > 0 ) {
usleep(1000);
printf("%s sells ticket:%d\n", id, ticket);
ticket--;
pthread\_mutex\_unlock(&mutex);
// sched\_yield(); 放弃CPU
} else {
pthread\_mutex\_unlock(&mutex);
break;
}
}
}
int main( void )
{
pthread\_t t1, t2, t3, t4;
pthread\_mutex\_init(&mutex, NULL);
pthread\_create(&t1, NULL, route, "thread 1");
pthread\_create(&t2, NULL, route, "thread 2");
**自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。**
**深知大多数Linux运维工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!**
**因此收集整理了一份《2024年Linux运维全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。**
![img](https://img-blog.csdnimg.cn/img_convert/fee31567d9fb6d1ec12bdef8c48ef908.png)
![img](https://img-blog.csdnimg.cn/img_convert/84d45fb0e2bf6b89043e6faa64e547d5.png)
![img](https://img-blog.csdnimg.cn/img_convert/bd7db223a5091d61f1e60487c7feaaf2.png)
![img](https://img-blog.csdnimg.cn/img_convert/9a481aea4b8e77a3a7c0f0fd188ccebd.png)
![img](https://img-blog.csdnimg.cn/img_convert/18abc9ae765b7c01ab9f2dcfb3f44815.png)
**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Linux运维知识点,真正体系化!**
**由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新**
**如果你觉得这些内容对你有帮助,可以添加VX:vip1024b (备注Linux运维获取)**
![img](https://img-blog.csdnimg.cn/img_convert/cebaaf839e03a1127ff5d0317efb8e19.jpeg)
为了做好运维面试路上的助攻手,特整理了上百道 **【运维技术栈面试题集锦】** ,让你面试不慌心不跳,高薪offer怀里抱!
这次整理的面试题,**小到shell、MySQL,大到K8s等云原生技术栈,不仅适合运维新人入行面试需要,还适用于想提升进阶跳槽加薪的运维朋友。**
![](https://img-blog.csdnimg.cn/img_convert/b7dacdb9c887af1443397b1ee74f35fa.png)
本份面试集锦涵盖了
* **174 道运维工程师面试题**
* **128道k8s面试题**
* **108道shell脚本面试题**
* **200道Linux面试题**
* **51道docker面试题**
* **35道Jenkis面试题**
* **78道MongoDB面试题**
* **17道ansible面试题**
* **60道dubbo面试题**
* **53道kafka面试**
* **18道mysql面试题**
* **40道nginx面试题**
* **77道redis面试题**
* **28道zookeeper**
**总计 1000+ 道面试题, 内容 又全含金量又高**
* **174道运维工程师面试题**
> 1、什么是运维?
> 2、在工作中,运维人员经常需要跟运营人员打交道,请问运营人员是做什么工作的?
> 3、现在给你三百台服务器,你怎么对他们进行管理?
> 4、简述raid0 raid1raid5二种工作模式的工作原理及特点
> 5、LVS、Nginx、HAproxy有什么区别?工作中你怎么选择?
> 6、Squid、Varinsh和Nginx有什么区别,工作中你怎么选择?
> 7、Tomcat和Resin有什么区别,工作中你怎么选择?
> 8、什么是中间件?什么是jdk?
> 9、讲述一下Tomcat8005、8009、8080三个端口的含义?
> 10、什么叫CDN?
> 11、什么叫网站灰度发布?
> 12、简述DNS进行域名解析的过程?
> 13、RabbitMQ是什么东西?
> 14、讲一下Keepalived的工作原理?
> 15、讲述一下LVS三种模式的工作过程?
> 16、mysql的innodb如何定位锁问题,mysql如何减少主从复制延迟?
> 17、如何重置mysql root密码?
**一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**
![img](https://img-blog.csdnimg.cn/img_convert/1e80d2cce6990c83b302ceccc9e19629.jpeg)
> 11、什么叫网站灰度发布?
> 12、简述DNS进行域名解析的过程?
> 13、RabbitMQ是什么东西?
> 14、讲一下Keepalived的工作原理?
> 15、讲述一下LVS三种模式的工作过程?
> 16、mysql的innodb如何定位锁问题,mysql如何减少主从复制延迟?
> 17、如何重置mysql root密码?
**一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**
[外链图片转存中...(img-cobfcawt-1712757486780)]