文章目录
- Ⅰ. 线程互斥概念
- Ⅱ. 互斥锁的概念
- Ⅲ. 互斥锁的接口
-
- 一、互斥锁的定义
- 二、初始化互斥锁
- 三、销毁互斥锁
- 四、互斥量的加锁和解锁
-
- ① 加锁接口
- ② 解锁接口
- 五、改进买票系统
- 💥注意事项
- Ⅳ. 互斥锁的实现原理
-
- 一、问题引入
- 二、复习知识
- 三、实现原理
- Ⅴ. 封装锁对象 && 守卫锁
- Ⅵ. 重入&&线程安全
-
- 一、概念
-
-
- ① 常见的可重入的情况
- ② 常见的不可重入的情况
- ③ 常见的线程安全的情况
- ④ 常见的线程不安全的情况
-
- 二、重入与线程安全的联系
- 三、重入与线程安全的区别

Ⅰ. 线程互斥概念
线程互斥(Thread Mutex
)是指在多线程并发执行的程序中,为了避免多个线程同时访问临界区(Critical Section
)而采用的一种同步机制。临界区是指对共享资源进行访问的代码段(也可以参考上面的概念),例如对共享变量的读写操作等。
线程互斥的目的是保证同一时刻只有一个线程能够进入临界区,以防止多个线程同时对共享资源进行修改,导致数据不一致或者结果出错。一般情况下,采用 互斥锁(Mutex
,也称为互斥量) 实现线程互斥,也可以使用其他同步机制,如**信号量(Semaphore
)、条件变量(Condition Variable
)**等。
基本概念总结:
- 临界资源:多线程执行流共享的资源就叫做临界资源。
- 临界区:每个线程内部,访问临界资源的代码,就叫做临界区。
- 互斥:任何时刻,互斥保证有且只有一个执行流进入临界区,访问临界资源,通常对临界资源起保护作用。
- 原子性:不会被任何调度机制打断的操作,该操作只有两态,要么完成,要么未完成。
下面我们就分别来学习互斥锁(也称为互斥量,为了统一,下面我们都称为互斥锁!)、条件变量、信号量和一些常见的锁情况!
Ⅱ. 互斥锁的概念
互斥锁(mutex
)是一种基本的线程同步机制,用于解决多个线程访问共享资源时可能发生的竞态条件问题。它可以 保证在同一时刻只有一个线程能够获取锁,并进入临界区。当一个线程获取到互斥锁时,其他线程尝试获取该锁时会被阻塞,直到该线程释放锁。
互斥锁提供了两个基本操作:加锁 && 解锁
- 加锁操作用于获取锁,防止其他线程进入临界区。
- 解锁操作用于释放锁,允许其他线程获取锁并进入临界区。
需要注意的是,在使用互斥锁时,应该注意死锁问题。死锁是指两个或多个线程相互等待对方释放锁,导致程序陷入无限等待的状态。 为了避免死锁问题,应该避免在临界区内调用阻塞操作(如等待信号量、等待条件变量等),并且应该尽可能减小临界区的范围,以避免锁竞争问题。
在 C++ 中,互斥量可以使用标准库中的
std::mutex
类来实现。std::mutex
类提供了两个基本操作:lock()
和unlock()
,分别用于获取锁定状态和释放锁定状态。当一个线程获取了锁定状态后,其他线程将不能再获取锁定状态,直到该线程释放锁定状态。 具体的内容请翻阅 C++ 笔记的讲解!
下面我们看一段代码,就能理解为何需要有互斥锁(下面的 thread.hpp
头文件是线程控制笔记中封装的线程库,可以去翻阅,这里直接调用):
#include "thread.hpp"
#include <memory>
#include <unistd.h>
int ticket = 10; // 全局变量
void* thread_routine(void* args)
{
string name = static_cast<const char*>(args);
while(true)
{
// 票数大于0则抢票,否则退出
if(ticket > 0)
{
usleep(12345); // 1秒=10^3毫秒=10^6微秒
cout << "i am new thread, name: " << name << ",the ticket: " << ticket << endl;
ticket--;
}
else
break;
}
}
int main()
{
unique_ptr<Thread> t1(new Thread(thread_routine, (void*)"user1", 1));
unique_ptr<Thread> t2(new Thread(thread_routine, (void*)"user2", 2));
unique_ptr<Thread> t3(new Thread(thread_routine, (void*)"user3", 3));
unique_ptr<Thread> t4(new Thread(thread_routine, (void*)"user4", 4));
unique_ptr<Thread> t5(new Thread(thread_routine, (void*)"user5", 5));
t1->join();
t2->join();
t3->join();
t4->join();
t5->join();
return 0;
}
上述代码是一个模拟抢票的系统,就是多个线程同时对全局变量 ticket
进行减减操作,而 ticket
也就是我们上面所说的临界资源啦,既然多个线程对 ticket 同时进行操作,那么势必会造成一些竞争条件问题,那么到底是怎么发生这个问题的呢❓❓❓
其实就是因为可能会发生这种情况:当多个线程同时来到 if(ticket > 0)
语句进行判断的时候,此时如果 ticket
已经是 1
了,那么此时假设 线程A 进入了这个语句,但是刚进入的时候就发生了一些情况比如说时间片切换、CPU
突然调度一个优先级更高的线程导致该 线程A 等待的情况,这个时候还没将 ticket
</