
// 操作共享变量会有问题的售票系统代码
#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);
}
⼀次执⾏结果:
thread 4 sells ticket:100
...
thread 4 sells ticket:1
thread 2 sells ticket:0
thread 1 sells ticket:-1
thread 3 sells ticket:-2为什么可能无法获得争取结果?
1.取出ticket--部分的汇编代码
2.objdump -d a.out > test.objdump
3.152 40064b: 8b 05 e3 04 20 00 mov 0x2004e3(%rip),%eax #
600b34 <ticket>
4.153 400651: 83 e8 01 sub $0x1,%eax
5.154 400654: 89 05 da 04 20 00 mov %eax,0x2004da(%rip) #
600b34 <ticket>-- 操作并不是原子操作,而是对应三条汇编指令:
要解决以上问题,需要做到三点:
要做到这三点,本质上就是需要⼀把锁。Linux上提供的这把锁叫互斥量。

初始化互斥量 初始化互斥量有两种方法:
1 pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER
1 int pthread_mutex_init(pthread_mutex_t *restrict mutex, const
pthread_mutexattr_t *restrict attr);
2 参数:
3 mutex:要初始化的互斥量
4 attr:NULL
销毁互斥量需要注意:
1 int pthread_mutex_destroy(pthread_mutex_t *mutex);互斥量加锁和解锁
1 int pthread_mutex_lock(pthread_mutex_t *mutex);
2 int pthread_mutex_unlock(pthread_mutex_t *mutex);
3 返回值:成功返回0,失败返回错误号调用pthread_ lock时,可能会遇到以下情况:
改进上面的售票系统:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <sched.h>
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");
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);
pthread_mutex_destroy(&mutex);
}
条件变量函数
初始化
1 int pthread_cond_init(pthread_cond_t *restrict cond,const pthread_condattr_t
*restrict attr);
2 参数:
3 cond:要初始化的条件变量
4 attr:NULL销毁
1 int pthread_cond_destroy(pthread_cond_t *cond) 等待条件满足
1 int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict
mutex);
2 参数:
3 cond:要在这个条件变量上等待
4 mutex:互斥量,后⾯详细解释唤醒等待
1 int pthread_cond_broadcast(pthread_cond_t *cond);
2 int pthread_cond_signal(pthread_cond_t *cond);简单案例:
#include <iostream>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
void *active( void *arg )
{
std::string name = static_cast<const char*>(arg);
while (true){
pthread_mutex_lock(&mutex);
pthread_cond_wait(&cond, &mutex);
std::cout << name << " 活动..." << std::endl;
pthread_mutex_unlock(&mutex);
}
}
int main( void )
{
pthread_t t1, t2;
pthread_create(&t1, NULL, active, (void*)"thread-1");
pthread_create(&t2, NULL, active, (void*)"thread-2");
sleep(3); // 可有可⽆,这⾥确保两个线程已经在运⾏
while(true)
{
// 对⽐测试
// pthread_cond_signal(&cond); // 唤醒⼀个线程
pthread_cond_broadcast(&cond); // 唤醒所有线程
sleep(1);
}
pthread_join(t1, NULL);
pthread_join(t2, NULL);
}$ ./cond
thread-1 活动...
thread-2 活动...
thread-1 活动...
thread-1 活动...
thread-2 活动..锁的原理:



申请⼀把锁是原子的,但是申请两把锁就不⼀定了

造成的结果是:




// 下⾯的C++不写了,理解就可以
#include <iostream>
#include <mutex>
#include <thread>
#include <vector>
#include <unistd.h>
// 定义两个共享资源(整数变量)和两个互斥锁
int shared_resource1 = 0;
int shared_resource2 = 0;
std::mutex mtx1, mtx2;
// ⼀个函数,同时访问两个共享资源
void access_shared_resources()
{
// std::unique_lock<std::mutex> lock1(mtx1, std::defer_lock);
// std::unique_lock<std::mutex> lock2(mtx2, std::defer_lock);
// // 使⽤ std::lock 同时锁定两个互斥锁
// std::lock(lock1, lock2);
// 现在两个互斥锁都已锁定,可以安全地访问共享资源
int cnt = 10000;
while (cnt)
{
++shared_resource1;
++shared_resource2;
cnt--;
}
// 当离开 access_shared_resources 的作⽤域时,lock1 和 lock2 的析构函数会被
⾃动调⽤
// 这会导致它们各⾃的互斥量被⾃动解锁
}
// 模拟多线程同时访问共享资源的场景
void simulate_concurrent_access()
{
std::vector<std::thread> threads;
// 创建多个线程来模拟并发访问
for (int i = 0; i < 10; ++i)
{
threads.emplace_back(access_shared_resources);
}
// 等待所有线程完成
for (auto &thread : threads)
{
thread.join();
}
// 输出共享资源的最终状态
std::cout << "Shared Resource 1: " << shared_resource1 << std::endl;
std::cout << "Shared Resource 2: " << shared_resource2 << std::endl;
}
int main()
{
simulate_concurrent_access();
return 0;
}1 $ ./a.out // 不⼀次申请
2 Shared Resource 1: 94416
3 Shared Resource 2: 945361 $ ./a.out // ⼀次申请
2 Shared Resource 1: 100000
3 Shared Resource 2: 100000