一、进程和线程的区别
1.进程是资源分配的最小单位,线程是CPU调度的最小单位,即程序执行的最小单位;
2.每个进程都有自己独立的地址空间,同一个进程的多个线程共用一个地址空间;
3.线程的上下文切换比进程要快;
二、多个线程共享和独占的资源(同一进程内)
同一个进程的多个线程是共用一个地址空间:
共享:代码段、堆区、全局数据区、打开的文件(文件描述符表)
独享:栈区,寄存器
(因为栈区存放的局部变量,函数参数,返回地址等信息)
三、同步和互斥的理解
互斥:某个资源只允许一个线程访问,具有排他性,访问是无序的,哪个线程先抢占到资源,哪个线程就执行,另外的线程等待;
同步:同步是在互斥的基础之上进行的,资源同样是具有唯一性,通过某种机制实现多个线程对一个资源的有序访问;
从上面的解释:同步是更为复杂的互斥,互斥可以理解为一种特殊的同步。
通俗例子:
两条流水线A,B用于装箱,一条流水线C用于封箱,其顺序是先进行装箱操作,才能进行后续的封箱操作。
A、B将装好的箱子,放到C的流水线上,则是先到现放,则A、B之间的关系就是互斥;
A、C之间的关系就是同步;
四、线程间同步的方式:
\qquad 同步的原因:针对线程中的共享资源,一般指全局变量和堆区等,也被称之为临界资源,保证数据的安全性,以及访问的顺序性。
一共有四种方式:
互斥量
读写锁
信号量
条件变量
1、互斥量
\qquad 互斥量即互斥锁( m u t e x mutex mutex),针对共享资源的个数,设置相应数量的锁。通过互斥锁,避免访问共享资源混乱的问题。
使用时,针对共享资源定义一个锁,在使用该资源时,使用lock加锁
步骤:
1.lock()
2.操作共享资源
3.unlock()
2、读写锁
\qquad 读写锁( r w l o c k rwlock rwlock)是互斥锁的升级,从名字可以看出,既可以锁定读操作,也可以锁定写操作,针对多线程,读锁是共享的,而写锁是独占的;
\qquad
当所有的线程都执行读操作时,加读写锁时,此时的状况就是所有的线程都可以并行执行读的操作;(若加的是互斥锁,读操作也是串行进行的);
若是
在加锁的时候分为:读锁 写锁
1.若一个线程加的是读锁 ( r d l o c k rdlock rdlock) ,另外的线程同样可以调用函数( r d l o c k rdlock rdlock)加锁,并且可以加锁成功,因为读锁是共享的。2.若一个线程加的是写锁( w r l o c k wrlock wrlock) ,另外的线程同样调用该函数( w r l o c k wrlock wrlock) 加锁,加锁失败,该线程会被阻塞,因为读锁是独占的;
注意:
当读写锁加的是写锁的状态时,其他线程尝试加锁都会被阻塞;
当读写锁加的是读锁的状态时,其他线程尝试加读锁都会成功,但是加写锁都会被阻塞;
3、条件变量
\qquad
条件变量(
c
o
n
d
i
t
o
n
conditon
conditon_
v
a
r
i
a
b
l
e
variable
variable)通常和互斥量一起使用;
\qquad
条件本身是由互斥量保护的。线程在改变条件状态之前必须首先锁住互斥量,其他线程咋获得锁之前不会察觉到这种改变,因为互斥量必须在锁定后才能计算条件。
\qquad
一般情况下,条件变量配合互斥锁用于处理生产者和消费者模型。(可以看我的另外的一篇博客:操作系统(C++)——生产者消费者模型)
4、信号量
信号量是一个与队列有关的整型变量;
执行PV操作
P操作:SemWait 申请资源 信号量n--
V操作:SemSignal 释放资源 信号量n++
当
n
>
0
n>0
n>0 :当前有可用的资源,可用的资源数量为
n
n
n;
当
n
=
0
n=0
n=0: 资源都被占用,没有可用的资源;
当
n
<
0
n<0
n<0:资源都被占用,并且还有
n
n
n个进程在排队;
从上面的解释可以知道:使用信号量达到了排队的目的,实现了访问者对资源的有序访问。
五、其他:
增加小知识:
自旋锁:与互斥锁类似,但是当自旋锁未加锁成功时,会一直处于忙等(自旋)的状态,一直占用CPU资源,若另外的线程锁的时间过长,会导致资源的浪费,所以自旋锁适用于锁被持有的时间短的场景。