知识回顾:
在前面章节介绍System 信号量时,我们介绍了共享资源、临界资源、互斥等概念,下面我们再来回顾一下
• 共享资源 多线程环境下被多个执行流共同访问的资源称为共享资源
• 临界资源 在多线程执行过程中需要被保护的共享资源称为临界资源
• 临界区 线程中访问临界资源的那段代码区域称为临界区
• 互斥机制 确保在任意时刻最多只有一个执行流能够进入临界区访问临界资源,从而实现对临界资源的保护
• 原子性操作 指不可被中断的操作,该类操作只有两种状态:要么完全执行完毕,要么尚未开始执行
在多线程编程中,线程使用的数据主要有两种类型:
当多个线程并发操作共享变量时,会导致数据竞争(Data Race)问题,表现为:
示例:
// 操作共享变量会有问题的售票系统代码
#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, (void *)"thread 1");
pthread_create(&t2, NULL, route, (void *)"thread 2");
pthread_create(&t3, NULL, route, (void *)"thread 3");
pthread_create(&t4, NULL, route, (void *)"thread 4");
pthread_join(t1, NULL);
pthread_join(t2, NULL);
pthread_join(t3, NULL);
pthread_join(t4, NULL);
}运行结果:
ltx@iv-ye1i2elts0wh2yp1ahah:~/gitLinux/Linux_system/lesson_thread/Mutex$ ./test
thread 2 sells ticket:100
thread 3 sells ticket:100
thread 1 sells ticket:100
thread 4 sells ticket:100
thread 2 sells ticket:96
thread 1 sells ticket:95
thread 3 sells ticket:94
thread 4 sells ticket:94
thread 2 sells ticket:92
thread 1 sells ticket:91
thread 4 sells ticket:90
thread 3 sells ticket:90
thread 2 sells ticket:88
thread 1 sells ticket:87
thread 3 sells ticket:86
thread 4 sells ticket:87
thread 2 sells ticket:84
thread 1 sells ticket:83
thread 3 sells ticket:83
thread 4 sells ticket:83
thread 2 sells ticket:80
thread 1 sells ticket:79
thread 3 sells ticket:79
thread 4 sells ticket:77
thread 2 sells ticket:76
thread 1 sells ticket:75
thread 4 sells ticket:74
thread 3 sells ticket:75
thread 2 sells ticket:72
thread 4 sells ticket:71
thread 1 sells ticket:71
thread 3 sells ticket:70
thread 2 sells ticket:68
thread 4 sells ticket:67
thread 3 sells ticket:66
thread 1 sells ticket:65
thread 2 sells ticket:64
thread 4 sells ticket:63
thread 3 sells ticket:62
thread 1 sells ticket:61
thread 2 sells ticket:60
thread 4 sells ticket:59
thread 3 sells ticket:58
thread 1 sells ticket:57
thread 2 sells ticket:56
thread 3 sells ticket:55
thread 4 sells ticket:54
thread 1 sells ticket:53
thread 2 sells ticket:52
thread 3 sells ticket:51
thread 4 sells ticket:50
thread 2 sells ticket:49
thread 3 sells ticket:48
thread 4 sells ticket:47
thread 2 sells ticket:46
thread 3 sells ticket:45
thread 4 sells ticket:44
thread 2 sells ticket:43
thread 3 sells ticket:42
thread 4 sells ticket:41
thread 2 sells ticket:40
thread 3 sells ticket:39
thread 4 sells ticket:38
thread 2 sells ticket:37
thread 3 sells ticket:36
thread 4 sells ticket:36
thread 2 sells ticket:34
thread 3 sells ticket:33
thread 4 sells ticket:32
thread 1 sells ticket:31
thread 2 sells ticket:30
thread 4 sells ticket:29
thread 3 sells ticket:28
thread 1 sells ticket:27
thread 2 sells ticket:26
thread 4 sells ticket:25
thread 3 sells ticket:24
thread 1 sells ticket:23
thread 2 sells ticket:22
thread 4 sells ticket:21
thread 3 sells ticket:20
thread 1 sells ticket:19
thread 2 sells ticket:18
thread 4 sells ticket:17
thread 3 sells ticket:17
thread 1 sells ticket:15
thread 2 sells ticket:14
thread 4 sells ticket:13
thread 3 sells ticket:12
thread 1 sells ticket:11
thread 2 sells ticket:10
thread 4 sells ticket:9
thread 3 sells ticket:8
thread 1 sells ticket:7
thread 2 sells ticket:6
thread 4 sells ticket:5
thread 3 sells ticket:4
thread 1 sells ticket:3
thread 2 sells ticket:2
thread 4 sells ticket:1
thread 3 sells ticket:0
thread 1 sells ticket:-1
thread 2 sells ticket:-2多个线程同时操作共享变量ticket导致了不可预测的结果,甚至出现了负数票数。
ticket--操作不是原子的。它对应三条汇编指令:
mov 0x2004e3(%rip),%eax # 将ticket从内存加载到寄存器
sub $0x1,%eax # 寄存器中的值减1
mov %eax,0x2004da(%rip) # 将新值写回内存考虑两个线程同时执行ticket--操作的可能序列:
时间点 | 线程1执行 | 线程2执行 | ticket值 |
|---|---|---|---|
t1 | mov (ticket), %eax | 100 | |
t2 | sub $1, %eax | 100 | |
t3 | mov (ticket), %eax | 100 | |
t4 | sub $1, %eax | 100 | |
t5 | mov %eax, (ticket) | 99 | |
t6 | mov %eax, (ticket) | 99 |
最终结果:两个线程各执行了一次减操作,但ticket只减少了1!
关键问题在于这两行代码:
if (ticket > 0) { // 检查阶段
// ...
ticket--; // 执行阶段
}这两个操作不是原子的,而且它们之间有一个usleep(1000)调用,这极大地增加了线程切换的可能性。
让我们通过一个具体的执行序列来看看负数是如何产生的。假设初始ticket = 1。
时间线分析
时间 | 线程1执行 | 线程2执行 | ticket值 | 说明 |
|---|---|---|---|---|
t1 | if (ticket > 0) → true | 1 | 线程1检查票数,发现还有1张票 | |
t2 | 被操作系统中断 | 1 | 线程1的时间片用完,切换到线程2 | |
t3 | if (ticket > 0) → true | 1 | 线程2也检查票数,同样发现还有1张票 | |
t4 | usleep(1000) | 1 | 线程2开始休眠 | |
t5 | 被中断 | 1 | 线程2休眠时被中断,切换回线程1 | |
t6 | usleep(1000) | 1 | 线程1开始休眠 | |
t7 | 被中断 | 1 | 线程1休眠时被中断,切换到线程2 | |
t8 | printf(...) | 1 | 线程2打印售票信息 | |
t9 | ticket-- | 0 | 线程2执行减操作,票数变为0 | |
t10 | 循环结束,退出 | 0 | 线程2完成任务 | |
t11 | 被唤醒 | 0 | 切换回线程1 | |
t12 | printf(...) | 0 | 线程1打印售票信息(但票数已经是0了!) | |
t13 | ticket-- | -1 | 线程1执行减操作,票数变为-1 |
ticket--不是原子操作。让我们看看这三条汇编指令如何导致问题:
mov 0x2004e3(%rip),%eax # 将ticket值从内存加载到eax寄存器
sub $0x1,%eax # 将eax寄存器中的值减1
mov %eax,0x2004da(%rip) # 将eax寄存器的新值写回内存假设ticket = 1,两个线程交错执行:
时间 | 线程1指令 | 线程2指令 | eax1 | eax2 | ticket |
|---|---|---|---|---|---|
t1 | mov (ticket), %eax1 | 1 | - | 1 | |
t2 | sub $1, %eax1 | 0 | - | 1 | |
t3 | mov (ticket), %eax2 | 0 | 1 | 1 | |
t4 | sub $1, %eax2 | 0 | 0 | 1 | |
t5 | mov %eax1, (ticket) | 0 | 0 | 0 | |
t6 | mov %eax2, (ticket) | 0 | 0 | 0 |
在这个序列中,两个线程都执行了减操作,但最终票数只减少了1,而不是2。
当有4个线程同时运行时,情况变得更加复杂。可能的执行序列:
ticket > 0(为真)
ticket > 0(为真)
ticket > 0(为真)
ticket--,票数减为0
ticket--,票数减为-1
ticket--,票数减为-2
usleep(1000)调用极大地增加了线程切换的概率,因为:
要解决共享变量并发访问问题,必须满足以下三个核心条件:
实现这些条件的关键在于引入锁机制,Linux系统中提供的这种锁称为互斥量(Mutex)。

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;int pthread_mutex_init(pthread_mutex_t *restrict mutex,
const pthread_mutexattr_t *restrict attr);mutex:指向要初始化的互斥量的指针
attr:互斥量属性,通常为NULL表示使用默认属性
使用示例
// 在函数内部动态初始化互斥量
pthread_mutex_t mutex;
if (pthread_mutex_init(&mutex, NULL) != 0) {
// 处理初始化失败
perror("Failed to initialize mutex");
exit(EXIT_FAILURE);
}int pthread_mutex_destroy(pthread_mutex_t *mutex);使用示例
// 使用动态初始化的互斥量后销毁
pthread_mutex_t mutex;
pthread_mutex_init(&mutex, NULL);
// 使用互斥量...
pthread_mutex_destroy(&mutex); // 不再需要时销毁int pthread_mutex_lock(pthread_mutex_t *mutex);int pthread_mutex_unlock(pthread_mutex_t *mutex);返回值处理
两个函数都返回0表示成功,非0表示错误。应该总是检查返回值:
if (pthread_mutex_lock(&mutex) != 0) {
// 处理错误
perror("Failed to lock mutex");
// 适当的错误处理
}
// 临界区代码
if (pthread_mutex_unlock(&mutex) != 0) {
// 处理错误
perror("Failed to unlock mutex");
// 适当的错误处理
}int pthread_mutex_trylock(pthread_mutex_t *mutex);示例
if (pthread_mutex_trylock(&mutex) == 0) {
// 成功获取锁,执行临界区代码
pthread_mutex_unlock(&mutex);
} else {
// 锁已被占用,执行其他操作
printf("Mutex is busy, doing something else...\n");
}int pthread_mutex_timedlock(pthread_mutex_t *restrict mutex,
const struct timespec *restrict abs_timeout);mutex:要锁定的互斥量
abs_timeout:绝对超时时间
示例
struct timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);
ts.tv_sec += 2; // 2秒后超时
if (pthread_mutex_timedlock(&mutex, &ts) == 0) {
// 成功获取锁
pthread_mutex_unlock(&mutex);
} else {
// 超时或错误
printf("Failed to acquire lock within 2 seconds\n");
}虽然通常使用NULL(默认属性),但有时可能需要自定义属性:
pthread_mutexattr_t attr;
pthread_mutex_t mutex;
// 初始化属性对象
pthread_mutexattr_init(&attr);
// 设置属性(例如设置为递归互斥量)
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
// 使用属性初始化互斥量
pthread_mutex_init(&mutex, &attr);
// 使用完后销毁属性对象
pthread_mutexattr_destroy(&attr);调用 pthread_mutex_lock 时,可能会遇到以下几种具体情况:
静态初始化:
// 操作共享变量会有问题的售票系统代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
int ticket = 100;
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; // 静态初始化
void *route(void *arg)
{
char *id = (char *)arg;
while (1)
{
pthread_mutex_lock(&lock);
if (ticket > 0)
{
usleep(1000);
printf("%s sells ticket:%d\n", id, ticket);
ticket--;
pthread_mutex_unlock(&lock);
}
else
{
pthread_mutex_unlock(&lock);
break;
}
}
return nullptr;
}
int main(void)
{
pthread_t t1, t2, t3, t4;
pthread_create(&t1, NULL, route, (void *)"thread 1");
pthread_create(&t2, NULL, route, (void *)"thread 2");
pthread_create(&t3, NULL, route, (void *)"thread 3");
pthread_create(&t4, NULL, route, (void *)"thread 4");
pthread_join(t1, NULL);
pthread_join(t2, NULL);
pthread_join(t3, NULL);
pthread_join(t4, NULL);
}运行结果:
ltx@iv-ye1i2elts0wh2yp1ahah:~/gitLinux/Linux_system/lesson_thread/Mutex$ ./test
thread 1 sells ticket:100
thread 1 sells ticket:99
thread 1 sells ticket:98
thread 1 sells ticket:97
thread 1 sells ticket:96
thread 1 sells ticket:95
thread 1 sells ticket:94
thread 1 sells ticket:93
thread 1 sells ticket:92
thread 1 sells ticket:91
thread 1 sells ticket:90
thread 1 sells ticket:89
thread 1 sells ticket:88
thread 1 sells ticket:87
thread 1 sells ticket:86
thread 1 sells ticket:85
thread 1 sells ticket:84
thread 1 sells ticket:83
thread 1 sells ticket:82
thread 1 sells ticket:81
thread 1 sells ticket:80
thread 1 sells ticket:79
thread 1 sells ticket:78
thread 1 sells ticket:77
thread 1 sells ticket:76
thread 1 sells ticket:75
thread 1 sells ticket:74
thread 1 sells ticket:73
thread 1 sells ticket:72
thread 1 sells ticket:71
thread 1 sells ticket:70
thread 1 sells ticket:69
thread 1 sells ticket:68
thread 1 sells ticket:67
thread 1 sells ticket:66
thread 1 sells ticket:65
thread 1 sells ticket:64
thread 1 sells ticket:63
thread 1 sells ticket:62
thread 1 sells ticket:61
thread 1 sells ticket:60
thread 1 sells ticket:59
thread 1 sells ticket:58
thread 1 sells ticket:57
thread 1 sells ticket:56
thread 1 sells ticket:55
thread 1 sells ticket:54
thread 1 sells ticket:53
thread 1 sells ticket:52
thread 1 sells ticket:51
thread 1 sells ticket:50
thread 1 sells ticket:49
thread 1 sells ticket:48
thread 1 sells ticket:47
thread 1 sells ticket:46
thread 1 sells ticket:45
thread 1 sells ticket:44
thread 1 sells ticket:43
thread 1 sells ticket:42
thread 1 sells ticket:41
thread 1 sells ticket:40
thread 1 sells ticket:39
thread 1 sells ticket:38
thread 1 sells ticket:37
thread 1 sells ticket:36
thread 1 sells ticket:35
thread 1 sells ticket:34
thread 1 sells ticket:33
thread 1 sells ticket:32
thread 1 sells ticket:31
thread 1 sells ticket:30
thread 1 sells ticket:29
thread 1 sells ticket:28
thread 1 sells ticket:27
thread 1 sells ticket:26
thread 1 sells ticket:25
thread 1 sells ticket:24
thread 1 sells ticket:23
thread 1 sells ticket:22
thread 1 sells ticket:21
thread 1 sells ticket:20
thread 1 sells ticket:19
thread 1 sells ticket:18
thread 1 sells ticket:17
thread 1 sells ticket:16
thread 1 sells ticket:15
thread 1 sells ticket:14
thread 1 sells ticket:13
thread 1 sells ticket:12
thread 1 sells ticket:11
thread 1 sells ticket:10
thread 1 sells ticket:9
thread 1 sells ticket:8
thread 1 sells ticket:7
thread 1 sells ticket:6
thread 1 sells ticket:5
thread 1 sells ticket:4
thread 1 sells ticket:3
thread 1 sells ticket:2
thread 1 sells ticket:1可以看到我们加了锁之后,只有线程1在访问临界资源,其余线程不能访问。
动态初始化:
// 操作共享变量会有问题的售票系统代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
int ticket = 100;
pthread_mutex_t lock;
void *route(void *arg)
{
char *id = (char *)arg;
while (1)
{
pthread_mutex_lock(&lock);
if (ticket > 0)
{
usleep(1000);
printf("%s sells ticket:%d\n", id, ticket);
ticket--;
pthread_mutex_unlock(&lock);
}
else
{
pthread_mutex_unlock(&lock);
break;
}
}
return nullptr;
}
int main(void)
{
pthread_mutex_init(&lock, nullptr); // 动态初始化
pthread_t t1, t2, t3, t4;
pthread_create(&t1, NULL, route, (void *)"thread 1");
pthread_create(&t2, NULL, route, (void *)"thread 2");
pthread_create(&t3, NULL, route, (void *)"thread 3");
pthread_create(&t4, NULL, route, (void *)"thread 4");
pthread_join(t1, NULL);
pthread_join(t2, NULL);
pthread_join(t3, NULL);
pthread_join(t4, NULL);
pthread_mutex_destroy(&lock); // 动态初始化需要显式销毁
}运行结果:
ltx@iv-ye1i2elts0wh2yp1ahah:~/gitLinux/Linux_system/lesson_thread/Mutex$ ./test
thread 1 sells ticket:100
thread 1 sells ticket:99
thread 1 sells ticket:98
thread 1 sells ticket:97
thread 1 sells ticket:96
thread 1 sells ticket:95
thread 1 sells ticket:94
thread 1 sells ticket:93
thread 1 sells ticket:92
thread 1 sells ticket:91
thread 1 sells ticket:90
thread 1 sells ticket:89
thread 1 sells ticket:88
thread 1 sells ticket:87
thread 1 sells ticket:86
thread 1 sells ticket:85
thread 1 sells ticket:84
thread 1 sells ticket:83
thread 1 sells ticket:82
thread 1 sells ticket:81
thread 1 sells ticket:80
thread 1 sells ticket:79
thread 1 sells ticket:78
thread 1 sells ticket:77
thread 1 sells ticket:76
thread 1 sells ticket:75
thread 1 sells ticket:74
thread 1 sells ticket:73
thread 1 sells ticket:72
thread 1 sells ticket:71
thread 1 sells ticket:70
thread 1 sells ticket:69
thread 1 sells ticket:68
thread 1 sells ticket:67
thread 1 sells ticket:66
thread 1 sells ticket:65
thread 1 sells ticket:64
thread 1 sells ticket:63
thread 1 sells ticket:62
thread 1 sells ticket:61
thread 1 sells ticket:60
thread 1 sells ticket:59
thread 1 sells ticket:58
thread 1 sells ticket:57
thread 1 sells ticket:56
thread 1 sells ticket:55
thread 1 sells ticket:54
thread 1 sells ticket:53
thread 1 sells ticket:52
thread 1 sells ticket:51
thread 1 sells ticket:50
thread 1 sells ticket:49
thread 1 sells ticket:48
thread 1 sells ticket:47
thread 1 sells ticket:46
thread 1 sells ticket:45
thread 1 sells ticket:44
thread 1 sells ticket:43
thread 1 sells ticket:42
thread 1 sells ticket:41
thread 1 sells ticket:40
thread 1 sells ticket:39
thread 1 sells ticket:38
thread 1 sells ticket:37
thread 1 sells ticket:36
thread 1 sells ticket:35
thread 1 sells ticket:34
thread 1 sells ticket:33
thread 1 sells ticket:32
thread 1 sells ticket:31
thread 1 sells ticket:30
thread 1 sells ticket:29
thread 1 sells ticket:28
thread 1 sells ticket:27
thread 1 sells ticket:26
thread 1 sells ticket:25
thread 1 sells ticket:24
thread 1 sells ticket:23
thread 1 sells ticket:22
thread 1 sells ticket:21
thread 1 sells ticket:20
thread 1 sells ticket:19
thread 1 sells ticket:18
thread 1 sells ticket:17
thread 1 sells ticket:16
thread 1 sells ticket:15
thread 1 sells ticket:14
thread 1 sells ticket:13
thread 1 sells ticket:12
thread 1 sells ticket:11
thread 1 sells ticket:10
thread 1 sells ticket:9
thread 1 sells ticket:8
thread 1 sells ticket:7
thread 1 sells ticket:6
thread 1 sells ticket:5
thread 1 sells ticket:4
thread 1 sells ticket:3
thread 1 sells ticket:2
thread 1 sells ticket:1经过上面多个线程并发执行i++或++i操作的例子,大家已经清楚地意识到这些看似简单的自增操作实际上并非原子操作。在并发环境下,由于这些操作包含读取、修改和写入三个步骤,中间可能被其他线程打断,因此会导致数据一致性问题。
为了实现互斥锁操作,确保临界区代码的原子性执行,大多数现代计算机体系结构都提供了特殊的硬件指令。其中最常见的是swap或exchange指令(在x86架构中称为XCHG指令),这条指令的作用是将寄存器中的数据和内存单元的数据进行原子性交换。由于这个操作在硬件层面被设计为单条不可分割的指令,因此可以保证其原子性
现代处理器架构提供了一条特殊的原子指令,通常称为"交换"或"比较并交换"指令:
; 假设我们有一个交换指令
xchg [memory], register这条指令原子性地完成以下操作:
由于这是一个单一指令,它在执行过程中不会被中断,保证了原子性。
下面是使用交换指令实现简单自旋锁的伪代码:
; 假设锁变量在内存地址 LOCK
; 初始值为0表示未锁定,1表示已锁定
acquire_lock:
mov eax, 1 ; 将1放入寄存器
xchg eax, [LOCK] ; 原子交换:将eax与LOCK的值交换
test eax, eax ; 测试eax的值
jnz acquire_lock ; 如果eax不为0(锁已被占用),继续尝试
ret ; 成功获取锁
release_lock:
mov eax, 0 ; 将0放入寄存器
mov [LOCK], eax ; 释放锁(设置为0)
ret"访问内存的总线周期也有先后"触及了多处理器系统中的关键问题。
当处理器执行原子指令时:
现代多核处理器使用复杂的缓存一致性协议(如MESI协议)来确保所有处理器核心看到相同的内存视图:
当执行原子操作时:
下面我们来模拟封装一个简易版本的互斥量
namespace MutexModule
{
class Mutex
{
public:
Mutex()
{
int n = pthread_mutex_init(&_mutex, nullptr);
if(n != 0)
{
perror("init failed");
}
}
void Lock()
{
int n = pthread_mutex_lock(&_mutex);
if(n != 0)
{
perror("lock failed");
}
}
void Unlock()
{
int n = pthread_mutex_unlock(&_mutex);
if(n != 0)
{
perror("unlock failed");
}
}
~Mutex()
{
int n = pthread_mutex_destroy(&_mutex);
if(n != 0)
{
perror("destroy failed");
}
}
private:
pthread_mutex_t _mutex;
};
}我们还可以使用RAII的方式,也就是和智能指针一样,来帮我们管理锁。
namespace MutexModule
{
class Mutex
{
public:
Mutex()
{
int n = pthread_mutex_init(&_mutex, nullptr);
if (n != 0)
{
perror("init failed");
}
}
void Lock()
{
int n = pthread_mutex_lock(&_mutex);
if (n != 0)
{
perror("lock failed");
}
}
void Unlock()
{
int n = pthread_mutex_unlock(&_mutex);
if (n != 0)
{
perror("unlock failed");
}
}
~Mutex()
{
int n = pthread_mutex_destroy(&_mutex);
if (n != 0)
{
perror("destroy failed");
}
}
private:
pthread_mutex_t _mutex;
};
// 采用RAII风格,进行管理
class LockGuard
{
public:
LockGuard(Mutex& mutex)
:_mutex(mutex)
{
_mutex.Lock();
}
~LockGuard()
{
_mutex.Unlock();
}
private:
Mutex& _mutex;
};
}下面我们来测试一下:
#include "Mutex.hpp"
#include <unistd.h>
using namespace MutexModule;
int ticket = 100;
Mutex mutex;
void *route(void *arg)
{
char *id = (char *)arg;
while (1)
{
LockGuard lock(mutex);
if (ticket > 0)
{
usleep(1000);
printf("%s sells ticket:%d\n", id, ticket);
ticket--;
}
else
{
break;
}
}
return nullptr;
}
int main(void)
{
pthread_t t1, t2, t3, t4;
pthread_create(&t1, NULL, route, (void *)"thread 1");
pthread_create(&t2, NULL, route, (void *)"thread 2");
pthread_create(&t3, NULL, route, (void *)"thread 3");
pthread_create(&t4, NULL, route, (void *)"thread 4");
pthread_join(t1, NULL);
pthread_join(t2, NULL);
pthread_join(t3, NULL);
pthread_join(t4, NULL);
return 0;
}运行结果:
ltx@iv-ye1i2elts0wh2yp1ahah:~/gitLinux/Linux_system/lesson_thread/Mutex/Lock$ ./test
thread 1 sells ticket:100
thread 1 sells ticket:99
thread 1 sells ticket:98
thread 1 sells ticket:97
thread 1 sells ticket:96
thread 1 sells ticket:95
thread 1 sells ticket:94
thread 1 sells ticket:93
thread 1 sells ticket:92
thread 1 sells ticket:91
thread 1 sells ticket:90
thread 1 sells ticket:89
thread 1 sells ticket:88
thread 1 sells ticket:87
thread 1 sells ticket:86
thread 1 sells ticket:85
thread 1 sells ticket:84
thread 1 sells ticket:83
thread 1 sells ticket:82
thread 1 sells ticket:81
thread 1 sells ticket:80
thread 1 sells ticket:79
thread 1 sells ticket:78
thread 1 sells ticket:77
thread 1 sells ticket:76
thread 1 sells ticket:75
thread 1 sells ticket:74
thread 1 sells ticket:73
thread 1 sells ticket:72
thread 1 sells ticket:71
thread 1 sells ticket:70
thread 1 sells ticket:69
thread 1 sells ticket:68
thread 1 sells ticket:67
thread 1 sells ticket:66
thread 1 sells ticket:65
thread 1 sells ticket:64
thread 1 sells ticket:63
thread 1 sells ticket:62
thread 1 sells ticket:61
thread 1 sells ticket:60
thread 1 sells ticket:59
thread 1 sells ticket:58
thread 1 sells ticket:57
thread 1 sells ticket:56
thread 1 sells ticket:55
thread 1 sells ticket:54
thread 1 sells ticket:53
thread 1 sells ticket:52
thread 1 sells ticket:51
thread 1 sells ticket:50
thread 1 sells ticket:49
thread 1 sells ticket:48
thread 1 sells ticket:47
thread 1 sells ticket:46
thread 1 sells ticket:45
thread 1 sells ticket:44
thread 1 sells ticket:43
thread 1 sells ticket:42
thread 1 sells ticket:41
thread 1 sells ticket:40
thread 1 sells ticket:39
thread 1 sells ticket:38
thread 1 sells ticket:37
thread 1 sells ticket:36
thread 1 sells ticket:35
thread 1 sells ticket:34
thread 1 sells ticket:33
thread 1 sells ticket:32
thread 1 sells ticket:31
thread 1 sells ticket:30
thread 1 sells ticket:29
thread 1 sells ticket:28
thread 1 sells ticket:27
thread 1 sells ticket:26
thread 1 sells ticket:25
thread 1 sells ticket:24
thread 1 sells ticket:23
thread 1 sells ticket:22
thread 1 sells ticket:21
thread 1 sells ticket:20
thread 1 sells ticket:19
thread 1 sells ticket:18
thread 1 sells ticket:17
thread 1 sells ticket:16
thread 1 sells ticket:15
thread 1 sells ticket:14
thread 1 sells ticket:13
thread 1 sells ticket:12
thread 1 sells ticket:11
thread 1 sells ticket:10
thread 1 sells ticket:9
thread 1 sells ticket:8
thread 1 sells ticket:7
thread 1 sells ticket:6
thread 1 sells ticket:5
thread 1 sells ticket:4
thread 1 sells ticket:3
thread 1 sells ticket:2
thread 1 sells ticket:1📌 RAII风格的互斥锁, C++11也有,比如:
std::mutex mtx; std::lock_guard<std::mutex> guard(mtx);
这种RAII(Resource Acquisition Is Initialization)风格的锁管理方式具有以下特点: