Linux下线程的同步与互斥

发布于:2025-03-03 ⋅ 阅读:(19) ⋅ 点赞:(0)

开头我们来写一个简单的程序,实现用多线程的方式来抢票,代码如下:

运行完结果后发现,我这只有一万张票,在if条件判断里我也做了判断,怎么会干到-2来了呢?

运用线程的理论知识我们知道,ticketnum在线程中属于共享资源,它不具有原子性,在我们需要对变量进行减减的时候,内存会将变量存入cpu中减减,然后再把减完后的内容写入内存里三步,

但减减完的数据还没写入内存的时候,进程就被切换了,也就是上面三步线程没做完,所以不具备原子性,当然我们做if判断的时候,也是一种计算也是要进cpu的,在最后数据都只是1的时候,四个线程都进去了,随着变量被减减,他就会被减到负数。为了避免这种情况,可以使用互斥锁;

一,mutex

锁的使用最好是在临界区内,锁最好涵盖代码少一点,要不然会影响运行效率,如果使用全局锁,可用PTHREAD_MUTEX_TNITIALIZER来初始化锁,这样只需要声明:

初始化好了怎么加锁?:

可以看到,加锁解锁都得在临界区内加,并且不能大块代码的加锁。

可以看到,对临界区加锁之后,变量减减就变成具有原子性的了,加锁的本质就是对资源进行预定,整体使用资源,如果申请锁的时候,锁被别人拿走了,这时候其他线程就会阻塞等待直到前面线程运行完再竞争,线程在访问临界区代码的时候,可以不可以切换??可以切换!!

我被切走的时候,别人能进来吗??不能!我是抱着锁,被切换的!!不就是串行吗!效率低的原因!原子性!上面是全局初始化锁的方式,不用我们用init和destory方法,下面就是用局部初始化锁的方式书写代码:

首先对锁进行初始化,再在结束位置销毁锁,

其结果也是一样的:

二,cond(同步)

根据mutex的学习,来映伸出下一个问题,当一个线程拿完锁回到队列中再次与其他线程竞争锁的时候,会有部分概率重复使用锁(饥饿问题),其他线程会长时间得不到资源,会导致程序运行效率不高,这时候可以使用同步的方法来解决,就好比排队一样,排完队领完东西还要再领就得重新在队伍后面排队,这就叫同步

同步的意义:

互斥可以保证安全性,但是安全不一定合理高效

同步是保证安全的情况下,让代码变得合理和高效。

首先我们先来了解对应接口:首先来介绍同步的初始化,看图:

可以发现,他和mutex的初始化是一样的,可以全局初始化也可以局部初始化,在全局使用初始化可以不用调用init和destory函数。

下一个接口,让指定的线程在指定的环境变量等待,如果别人不唤醒就一直等。

signal接口,可以唤醒在特定条件变量下的一个线程:

broadcast可以唤醒特定条件变量下指定的所有线程。

条件变量是一个用来进行线程同步的特性,内部要维护线程队列。

下面利用之前的抢票代码来做线程同步的接口练习:

可以看到,我先把票数设为零,线程都会去wait这里等待:

可以看到线程都在阻塞等待,这时候我们可以通过放票然后唤醒线程的方式来抢票:

可以看到线程被唤醒但是没执行,是因为在wait那里会重新申请和释放锁,就相当于已经有锁了但是又去申请锁,代码就会被挂起,这个后面细讲,在while那边再次解锁来让我们看到测试结果先: