进程同步
临界区
对共享资源进行访问的那段代码称为临界区,当有其他进程在这段代码中运行时,其他进程就不能在这段代码中执行。
为了互斥访问临界资源,每个进程在进入临界区之前,需要先进行检查。
// entry section
// critical section
// exit section
互斥与同步
- 同步:多个进程的一定的先后执行关系。一组并发进程因直接制约而互相发送消息、进行互相合作、互相等待,使得各进程按一定的速度执行的过程。比如消费者需要等待仓库存在货物才可来消费,生产者在仓库没有摆满的情况下才能生产。
- 互斥:多个进程在同一时刻只有一个进程能进入临界区。也就是说,一个进程正在访问临界资源,其他要访问该资源的进程必须等待。比如消费者和生产者不能同时访问仓库。
信号量
信号量(Semaphore)是一个整型变量,可以对其执行wait和post等操作。
- wait:如果信号量大于 0 ,执行-1操作;如果信号量等于0,进程被阻塞,等待信号量大于0继续执行
- post:对信号量执行+1操作
- init:初始化信号量
- destory:销毁信号量
wait和post操作需要被设计成原子操作,不可分割,通常的做法是在执行这些操作的时候屏蔽中断。原子性保证了并发进程的隔离。
二元信号量即信号量的取值只能为0或者1,那么就成为了互斥量(Mutex),0表示临界区已经加锁,1表示临界区解锁。
经典模型
生产者消费者
使用一个缓冲区来保存物品,只有缓冲区没有满,生产者才可以放入物品;只有缓冲区不为空,消费者才可以拿走物品。一个时间只能有一个人访问缓冲区。
为了同步生产者和消费者的行为,需要记录缓冲区中物品的数量。数量可以使用信号量来进行统计,这里需要使用两个信号量:empty_sem记录空缓冲区的数量,full_sem记录满缓冲区的数量。其中,empty_sem信号量是在生产者进程中使用,当empty_sem不为 0 时,生产者才可以放入物品;full_sem信号量是在消费者进程中使用,当full_sem信号量不为0时,消费者才可以取走物品。
不论是生产者还是消费者,都不能先抢占互斥信号量再等待同步信号量,如果不这样做可能会发送这种情况:生产者首先进入了仓库,而此时仓库是满的,那么生产者就无法生产产品而等待消费者消费。消费者因为生产者占据着仓库,因此无法进入仓库消费,这样就导致导致生产者永远被阻塞,不会释放锁,消费者因此也会永远等待下去,发生死锁的情况。
/* 生产者消费者示例程序 */
#include <iostream>
#include <pthread.h>
#include <vector>
#include <semaphore.h>
#include <windows.h>
using namespace std;
// 仓库容量
#define MAX_PRODUCTS 32
// 生产者数量
#define PRODUCER 4
// 消费者数量
#define CONSUMER 2
// 线程类型
enum type
{
prod,
cons
};
// 传递给线程的参数
struct params
{
int id;
int type;
};
// 模拟仓库
vector<int> product_id;
// 互斥信号量
sem_t syn_sem;
// 同步信号
sem_t empty_sem;
sem_t full_sem;
// 初始化信号量
void init_sem()
{
sem_init(&syn_sem, 0, 1);
sem_init(&empty_sem, 0, MAX_PRODUCTS);
sem_init(&full_sem, 0, 0);
}
// 销毁信号量
void destory_sem()
{
sem_destroy(&syn_sem);
sem_destroy(&empty_sem);
sem_destroy(&full_sem);
}
// 生产者线程轨迹
void *producer(void *args)
{
struct params *info = (struct params *)args;
int pro_id = info->id;
while (1)
{
Sleep(1000);
// 等待仓库可取
sem_wait(&empty_sem);
// 等待仓库无人
sem_wait(&syn_sem);
int id = product_id.size();
cout << "[prod] producer " << pro_id << " produce a product, id is " << id << endl;
product_id.emplace_back(id);
// 告知仓库可取
sem_post(&full_sem);
// 离开仓库
sem_post(&syn_sem);
}
}
// 消费者线程轨迹
void *consumer(void *args)
{
struct params *info = (struct params *)args;
int con_id = info->id;
while (1)
{
Sleep(1000);
// 等待仓库可取
sem_wait(&full_sem);
// 等待仓库无人
sem_wait(&syn_sem);
int id = product_id.back();
product_id.pop_back();
cout << "[consumer] consumer " << con_id << " consume a product, id is " << id << endl;
// 告知仓库可放
sem_post(&empty_sem);
// 离开仓库
sem_post(&syn_sem);
}
}
int main()
{
// 初始化线程
init_sem();
pthread_t threads[PRODUCER + CONSUMER];
// 创建生成者线程
for (int i = 0; i < PRODUCER; i++)
{
// 构造参数
struct params *args = NULL;
args = new struct params;
args->id = i;
args->type = prod;
if (pthread_create(&threads[i], NULL, producer, (void *)args) != 0)
{
cout << "create a thread error" << endl;
}
}
// 创建消费者线程
for (int i = 0; i < CONSUMER; i++)
{
// 构造参数
struct params *args = NULL;
args = new struct params;
args->id = i;
args->type = cons;
if (pthread_create(&threads[i], NULL, consumer, (void *)args) != 0)
{
cout << "create a thread error" << endl;
}
}
// 等待线程完成主线程再结束
pthread_exit(NULL);
// 销毁信号量
destory_sem();
}
运行结果如下
[prod] producer 3 produce a product, id is 0
[prod] producer 1 produce a product, id is 1
[prod] producer 0 produce a product, id is 2
[prod] producer 2 produce a product, id is 3
[consumer] consumer 1 consume a product, id is 3
[consumer] consumer 0 consume a product, id is 2
[prod] producer 0 produce a product, id is 2
[prod] producer 1 produce a product, id is 3
[consumer] consumer 0 consume a product, id is 3
[prod] producer 3 produce a product, id is 3
[prod] producer 2 produce a product, id is 4
[consumer] consumer 1 consume a product, id is 4
[prod] producer 2 produce a product, id is 4
[prod] producer 3 produce a product, id is 5
[prod] producer 1 produce a product, id is 6
[consumer] consumer 0 consume a product, id is 6
[consumer] consumer 1 consume a product, id is 5
[prod] producer 0 produce a product, id is 5
[prod] producer 2 produce a product, id is 6
[prod] producer 3 produce a product, id is 7
[prod] producer 1 produce a product, id is 8
[prod] producer 0 produce a product, id is 9
[consumer] consumer 1 consume a product, id is 9
[consumer] consumer 0 consume a product, id is 8
……
……
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END