一、生产者 - 消费者问题
- 环境:windows
问题描述:
一组生产者进程和一组消费者进程共享一个初始为空、大小为〃的缓冲区,只有缓冲区没满时,生产者才能把消息放入缓冲区,否则必须等待;只有缓冲区不空时,消费者才能从中取出消息,否则必须等待。由于缓冲区是临界资源,它只允许一个生产者放入消息,或一个消费者从中取出消息。
代码:文章来源:https://www.toymoban.com/news/detail-422837.html
#include <Windows.h>
#include <iostream>
#include <stdlib.h>
typedef HANDLE Semaphore;
#define P(S) WaitForSingleObject(S, INFINITE)
#define V(S) ReleaseSemaphore(S, 1, NULL)
int produceId = 1000; //生产的的产品ID
int consumeId; //消费的产品ID
int buf[10]; //用于存放缓存中的数据
int BUFSIZE = 10; //缓存大小
int in = 0; //用于表示生产者放入缓存的下表索引
int out = 0; //用于表示消费者从缓存中那组的数据的下标索引
//初始化empty互斥量,初值为10,表示空的缓存数
Semaphore empty = CreateSemaphore(
NULL,
10,
10,
NULL
);
//初始化互斥量,用户互斥访问临界资源
Semaphore mutex = CreateSemaphore(
NULL,
1,
1,
NULL
);
//初始化full互斥量,初值为0,用于表示缓存中放了多少份数据
Semaphore full = CreateSemaphore(
NULL,
0,
10,
NULL
);
DWORD WINAPI Producer(LPVOID); //生产者线程函数
DWORD WINAPI Consumer(LPVOID); //消费者线程函数
VOID Produce(); //生产者生产数据 的函数
VOID Consume(); //消费者消费数据的函数
VOID printbuf(); //用于打印缓存中的数据的函数
int main()
{
HANDLE produce;
HANDLE consumer1, consumer2, consumer3;
produce = CreateThread(
NULL,
0,
Producer,
NULL,
0,
NULL
);
consumer1 = CreateThread(
NULL,
0,
Consumer,
NULL,
0,
NULL
);
consumer2 = CreateThread(
NULL,
0,
Consumer,
NULL,
0,
NULL
);
consumer3 = CreateThread(
NULL,
0,
Consumer,
NULL,
0,
NULL
);
while (1)
{
WaitForSingleObject(produce, INFINITE);
WaitForSingleObject(consumer1, INFINITE);
WaitForSingleObject(consumer2, INFINITE);
WaitForSingleObject(consumer3, INFINITE);
}
return 0;
}
VOID printbuf() {
std::cout << "缓存中的数据:";
bool hasData = false;
for (int i = 0; i < BUFSIZE; i++) {
if (buf[i] != 0) {
std::cout << buf[i] << "----";
hasData = true;
}
}
if (hasData)
std::cout << std::endl;
else
std::cout << "无" << std::endl;
}
VOID Produce() {
produceId++;
buf[in++] = produceId;
std::cout << "我是生产者,我生产了:" << produceId << std::endl;
printbuf();
in %= BUFSIZE;
}
VOID Consume() {
consumeId = buf[out];
std::cout << "我是消费者,我消费了:" << consumeId << std::endl;
buf[out++] = 0;
printbuf();
out %= BUFSIZE;
}
DWORD WINAPI Producer(LPVOID) {
while (1) {
P(empty); //有无空缓存
P(mutex); //互斥夹紧临界资源
Produce(); //生产数据
Sleep(1000);
V(mutex); //互斥夹紧临界资源
V(full); //full值+1
}
return 0;
}
DWORD WINAPI Consumer(LPVOID) {
while (1) {
P(full); //缓存中有无数据
P(mutex); //互斥夹紧临界资源
Consume(); //消费数据
Sleep(1000);
V(mutex); //互斥夹紧临界资源
V(empty); //空缓存数加1
}
return 0;
}
运行效果:
再来看一个复杂一点的生产者 - 消费者问题:
桌子上有一个盘子,每次只能向其中放入一个水果。爸爸专向盘子中放苹果,妈妈专向盘子中放橘子,儿子专等吃盘子中的橘子,女儿专等吃盘子中的苹果。只有盘子为空时, 爸爸或妈妈才可向盘子中放一个水果;仅当盘子中有自己需要的水果时,儿子或女儿可以从盘子中取岀。
代码:
#include <Windows.h>
#include <iostream>
#include <stdlib.h>
typedef HANDLE Semaphore;
#define P(S) WaitForSingleObject(S, INFINITE)
#define V(S) ReleaseSemaphore(S, 1, NULL)
#define _NULL 0
#define _APPLE 1
#define _ORANGE 2
#define _FATHER 1
#define _MOTHER 2
#define _SON 3
#define _DAUGHTER 4
int what = 0; //用于表示是哪种水果
int who = 0; //用于表示是哪位放了或者吃了水果
//初始化apple互斥量,初值为0,用于表示盘中是否放了苹果
Semaphore apple = CreateSemaphore(
NULL,
0,
1,
NULL
);
//初始化orange互斥量,初值为0,用于表示盘中是否放了橘子
Semaphore orange = CreateSemaphore(
NULL,
0,
1,
NULL
);
//初始化plate互斥量,用于表示盘中是否有水果
Semaphore plate = CreateSemaphore(
NULL,
1,
1,
NULL
);
DWORD WINAPI Father(LPVOID); //父亲线程函数
DWORD WINAPI Mother(LPVOID); //母亲线程函数
DWORD WINAPI Son(LPVOID); //儿子线程函数
DWORD WINAPI Daughter(LPVOID); //女儿线程函数
VOID Put(int); //父母放水果的函数
VOID Get(); //儿女吃水果的函数
VOID PrintPlate(); //用于打印的函数
int main()
{
HANDLE father, mother, son, daughter;;
father = CreateThread(
NULL,
0,
Father,
NULL,
0,
NULL
);
mother = CreateThread(
NULL,
0,
Mother,
NULL,
0,
NULL
);
son = CreateThread(
NULL,
0,
Son,
NULL,
0,
NULL
);
daughter = CreateThread(
NULL,
0,
Daughter,
NULL,
0,
NULL
);
while (1)
{
WaitForSingleObject(father, INFINITE);
WaitForSingleObject(mother, INFINITE);
WaitForSingleObject(son, INFINITE);
WaitForSingleObject(daughter, INFINITE);
}
return 0;
}
VOID PrintPlate() {
std::string _who[4] = {
"爸爸",
"妈妈",
"儿子",
"女儿"
};
std::string _what[2] = {
"苹果",
"橘子"
};
if (who == _FATHER || who == _MOTHER) {
std::cout << "我是:" << _who[who - 1].c_str()
<< "; 我放了【" << _what[what - 1].c_str() << "】在盘子里...."
<< std::endl;
}
else {
std::cout << "我是:" << _who[who - 1].c_str()
<< "; 我从盘子里把【" << _what[what - 1].c_str() << "】拿走了...."
<< std::endl;
}
}
VOID Put(int id) {
what = (id == _APPLE) ? _APPLE : _ORANGE;
who = (id == _FATHER) ? _FATHER : _MOTHER;
PrintPlate();
}
VOID Get() {
who = (what == _APPLE) ? _DAUGHTER : _SON;
PrintPlate();
what = _NULL;
}
DWORD WINAPI Father(LPVOID) {
while (1) {
P(plate); //看盘子是否为空
Put(_APPLE); //父亲放入苹果
Sleep(1000);
V(apple); //唤醒女儿线程
}
return 0;
}
DWORD WINAPI Mother(LPVOID) {
while (1) {
P(plate); //看盘子是否为空
Put(_ORANGE); //母亲放入橘子
Sleep(1000);
V(orange); //唤醒儿子线程
}
return 0;
}
DWORD WINAPI Son(LPVOID) {
while (1) {
P(orange); //看盘子里是否有橘子
Get(); //拿走橘子
Sleep(1000);
V(plate); //盘子变空,唤醒父母
}
return 0;
}
DWORD WINAPI Daughter(LPVOID) {
while (1) {
P(apple); //看盘子里是否有苹果
Get(); //拿走苹果
Sleep(1000);
V(plate); //盘子变空,唤醒父母
}
return 0;
}
运行效果:
二、读者 - 写者问题
问题描述:
有读者和写者两组并发进程,共享一个文件,当两个或以上的读进程同时访问共享数据时不会产生副作用,但若某个写进程和其他进程(读进程或写进程)同时访问共享数据时则可能导致数据不一致的错误。因此要求:①允许多个读者可以同时对文件执行读操作;②只允许一个写者往文件中写信息;③任一写者在完成写操作之前不允许其他读者或写者工作;④写者执行写操作前,应让已有的读者和写者全部退出。
读优先(读优先可能会导致写者线程饿死)的代码:
//读者优先,读者优先可能导致写进程饿死
#include <Windows.h>
#include <iostream>
#include <stdlib.h>
typedef HANDLE Semaphore;
#define P(S) WaitForSingleObject(S, INFINITE)
#define V(S) ReleaseSemaphore(S, 1, NULL)
int count = 0; //用于记录读者数量
//初始化mutex互斥量,用于保证互斥访问count
Semaphore mutex = CreateSemaphore(
NULL,
1,
1,
NULL
);
//初始化rw互斥量,用于保证读写进程 互斥地访问文件
Semaphore rw = CreateSemaphore(
NULL,
1,
1,
NULL
);
DWORD WINAPI Reader(LPVOID); //读者线程函数
DWORD WINAPI Writer(LPVOID); //写者线程函数
VOID Read(); //读函数
VOID Write(); //写函数
int main()
{
HANDLE reader[5];
HANDLE writer[5];
for (int i = 0; i < 5; i++) {
reader[i] = CreateThread(
NULL,
0,
Reader,
NULL,
0,
NULL
);
}
for (int i = 0; i < 5; i++) {
writer[i] = CreateThread(
NULL,
0,
Writer,
NULL,
0,
NULL
);
}
while (1)
{
for (int i = 0; i < 20; i++)
WaitForSingleObject(reader[i], INFINITE);
for (int i = 0; i < 5; i++)
WaitForSingleObject(writer[i], INFINITE);
}
return 0;
}
VOID Read() {
std::cout << "我是读线程,我正在读......." << std::endl;
}
VOID Write() {
std::cout << "我是写线程,我正在写---------------" << std::endl;
}
DWORD WINAPI Reader(LPVOID) {
while (1) {
P(mutex); //互斥访问count变量
if (count == 0) //当第一个读线程读共享文件时
P(rw); //阻塞写线程
count++; //读线程数加 1
Sleep(1000);
V(mutex); //互斥访问count变量
Read(); //读取共享文件
P(mutex); //互斥访问count变量
count--; //读线程数减 1
if (count == 0) //当当前没有读线程工作时
V(rw); //释放rw信号量,通知写线程可以工作了
V(mutex); //互斥访问count变量
}
return 0;
}
DWORD WINAPI Writer(LPVOID) {
while (1) {
P(rw); //保证互斥访问共享文件
Write(); //写共享文件
Sleep(300);
V(rw); //保证互斥访问共享文件
}
return 0;
}
运行效果:
注:可以看到,由于读者线程优先,导致了写者线程饿死
写优先,或者叫读写平衡的代码:
//写者优先,或者叫读写平衡
#include <Windows.h>
#include <iostream>
#include <stdlib.h>
typedef HANDLE Semaphore;
#define P(S) WaitForSingleObject(S, INFINITE)
#define V(S) ReleaseSemaphore(S, 1, NULL)
int count = 0; //用于记录读者数量
//初始化mutex互斥量,用于保证互斥访问count
Semaphore mutex = CreateSemaphore(
NULL,
1,
1,
NULL
);
//初始化rw互斥量,用于保证读写进程 互斥地访问文件
Semaphore rw = CreateSemaphore(
NULL,
1,
1,
NULL
);
//初始化w互斥量,用于实现“写优先”
Semaphore w = CreateSemaphore(
NULL,
1,
1,
NULL
);
DWORD WINAPI Reader(LPVOID); //读者线程函数
DWORD WINAPI Writer(LPVOID); //写者线程函数
VOID Read(); //读函数
VOID Write(); //写函数
int main()
{
HANDLE reader[5];
HANDLE writer[5];
for (int i = 0; i < 5; i++) {
reader[i] = CreateThread(
NULL,
0,
Reader,
NULL,
0,
NULL
);
}
for (int i = 0; i < 5; i++) {
writer[i] = CreateThread(
NULL,
0,
Writer,
NULL,
0,
NULL
);
}
while (1)
{
for (int i = 0; i < 20; i++)
WaitForSingleObject(reader[i], INFINITE);
for (int i = 0; i < 5; i++)
WaitForSingleObject(writer[i], INFINITE);
}
return 0;
}
VOID Read() {
std::cout << "我是读线程,我正在读......." << std::endl;
}
VOID Write() {
std::cout << "我是写线程,我正在写---------------" << std::endl;
}
DWORD WINAPI Reader(LPVOID) {
while (1) {
P(w); //保证在无写线程时进入
P(mutex); //互斥访问count变量
if (count == 0) //当第一个读线程读共享文件时
P(rw); //阻塞写线程
count++; //读线程数加 1
Sleep(1000);
V(mutex); //互斥访问count变量
V(w); //释放w,保证写线程优先
Read(); //读取共享文件
P(mutex); //互斥访问count变量
count--; //读线程数减 1
if (count == 0) //当当前没有读线程工作时
V(rw); //释放rw信号量,通知写线程可以工作了
V(mutex); //互斥访问count变量
}
return 0;
}
DWORD WINAPI Writer(LPVOID) {
while (1) {
P(w); //在无写线程时进入
P(rw); //保证互斥访问共享文件
Write(); //写共享文件
Sleep(300);
V(rw); //保证互斥访问共享文件
V(w); //写完毕,释放
}
return 0;
}
运行效果:
注:可以看到写者线程被调度了
三、哲学家进餐问题
问题描述:
一张圆桌边上坐着5名哲学家,每两名哲学家之间的桌上摆一根筷子,两根筷子中间是一碗米饭,如图2.12所示。哲学家们倾注毕生精力用于思考和进餐,哲学家在思考时,并不影响他人。只有当哲学家饥饿时,才试图拿起左、右两根筷子(一根一根地拿起)。若筷子已在他人手上,则需要等待。饥饿的哲学家只有同时拿到了两根筷子才可以开始进餐,进餐完毕后,放下筷子继续思考。
代码:
#include <Windows.h>
#include <iostream>
#include <stdlib.h>
typedef HANDLE Semaphore;
#define P(S) WaitForSingleObject(S, INFINITE)
#define V(S) ReleaseSemaphore(S, 1, NULL)
//筷子互斥量,初值为1,表示是否有筷子
Semaphore chopstick[5];
//初始化mutex互斥量,用于保证互斥访问chopstick
Semaphore mutex = CreateSemaphore(
NULL,
1,
1,
NULL
);
DWORD WINAPI Philosopher(LPVOID); //哲学家线程函数
VOID InitSemaphore();
VOID Eat(int); //哲学家进餐函数
VOID Think(int); //哲学家思考函数
int main()
{
InitSemaphore();
HANDLE philosopher[5];
int id[5];
for (int i = 0; i < 5; i++)
id[i] = i;
for (int i = 0; i < 5; i++) {
philosopher[i] = CreateThread(
NULL,
0,
Philosopher,
(LPVOID)&(id[i]),
0,
NULL
);
}
while (1)
{
for (int i = 0; i < 5; i++)
WaitForSingleObject(philosopher[i], INFINITE);
}
return 0;
}
VOID InitSemaphore() {
for (int i = 0; i < 5; i++) {
chopstick[i] = CreateSemaphore(
NULL,
1,
1,
NULL
);
}
}
VOID Eat(int i) {
std::cout << "哲学家【" << i + 1 << "】正在进餐..." << std::endl;
}
VOID Think(int i) {
std::cout << "哲学家【" << i + 1 << "】正在思考........" << std::endl;
}
DWORD WINAPI Philosopher(LPVOID i) {
while (1) {
P(mutex); //互斥访问筷子
P(chopstick[*((int *)i)]); //拿起左手边的筷子
P(chopstick[((*((int *)i)) + 1) % 5]); //拿起右手边的筷子
V(mutex); //互斥访问筷子
Eat(*(int *)i); //进餐
Sleep(1000);
V(chopstick[*((int *)i)]); //放下左手边的筷子
V(chopstick[((*((int *)i) + 1)) % 5]); //放下右手边的筷子
Think(*(int *)i); //思考.....在这里可能发生竟态条件
}
return 0;
}
运行效果:
注:以上思考的输出出现混乱的原因:
在一个哲学家线程调用到 Think 函数的时候由于没有信号量的保护,因此可能发生竟态条件,由于线程异步地执行,可能在某个线程运行 Think 的时候发生了调度。
四、吸烟者问题
问题描述:
假设一个系统有三个抽烟者进程和一个供应者进程。每个抽烟者不停地卷烟并抽掉它,但要卷起并抽掉一支烟,抽烟者需要有三种材料:烟草、纸和胶水。三个抽烟者中,第一个拥有烟草,第二个拥有纸,第三个拥有胶水。供应者进程无限地提供三种材料,供应者每次将两种材料放到桌子上,拥有剩下那种材料的抽烟者卷一根烟并抽掉它,并给供应者一个信号告诉已完成,此时供应者就会将另外两种材料放到桌上,如此重复(让三个抽烟者轮流地抽烟)。
代码:
#include <Windows.h>
#include <iostream>
#include <stdlib.h>
typedef HANDLE Semaphore;
#define P(S) WaitForSingleObject(S, INFINITE)
#define V(S) ReleaseSemaphore(S, 1, NULL)
#define YanCaoAndZhi 1
#define YanCaoAndJiaoShui 2
#define ZhiAndJiaoShui 3
int num; //供应者供应用的 随机数
int what; //表示桌上有什么材料
//初始化offerYanCao互斥量,用于表示供应烟草和纸
Semaphore offerYanCaoAndZhi = CreateSemaphore(
NULL,
0,
1,
NULL
);
//初始化offerZhi互斥量,用于表示供应烟草和胶水
Semaphore offerYanCaoAndJiaoShui = CreateSemaphore(
NULL,
0,
1,
NULL
);
//初始化offerJiaoShui互斥量,用于表示供应纸和胶水
Semaphore offerZhiAndJiaoShui = CreateSemaphore(
NULL,
0,
1,
NULL
);
//初始化finish互斥量,用于表示吸烟完成
Semaphore finish = CreateSemaphore(
NULL,
0,
1,
NULL
);
DWORD WINAPI Producer(LPVOID); //供应者线程函数
DWORD WINAPI Smoker1(LPVOID); //1号吸烟者,拥有烟草
DWORD WINAPI Smoker2(LPVOID); //2号吸烟者,拥有纸
DWORD WINAPI Smoker3(LPVOID); //3号吸烟者,拥有胶水
VOID Put(int); //供应者放材料
VOID Smoke(); //吸烟者吸烟
int main()
{
HANDLE producer;
HANDLE smoker1, smoker2, smoker3;
producer = CreateThread(
NULL,
0,
Producer,
NULL,
0,
NULL
);
smoker1 = CreateThread(
NULL,
0,
Smoker1,
NULL,
0,
NULL
);
smoker2 = CreateThread(
NULL,
0,
Smoker2,
NULL,
0,
NULL
);
smoker3 = CreateThread(
NULL,
0,
Smoker3,
NULL,
0,
NULL
);
while (1)
{
WaitForSingleObject(producer, INFINITE);
WaitForSingleObject(smoker1, INFINITE);
WaitForSingleObject(smoker2, INFINITE);
WaitForSingleObject(smoker3, INFINITE);
}
return 0;
}
VOID Put(int i) {
if (i == YanCaoAndZhi) {
what = YanCaoAndZhi;
std::cout << "我是供应者,我现在要把【烟草】和【纸】放到桌上" << std::endl;
}
else if (i == YanCaoAndJiaoShui) {
what = YanCaoAndJiaoShui;
std::cout << "我是供应者,我现在要把【烟草】和【胶水】放到桌上" << std::endl;
}
else {
what = ZhiAndJiaoShui;
std::cout << "我是供应者,我现在要把【纸】和【胶水】放到桌上" << std::endl;
}
}
VOID Smoke() {
if (what == YanCaoAndZhi) {
std::cout << "我是吸烟者【3】号,我拥有【胶水】,我现在从桌上拿走【烟草】和【纸】,我开始吸烟了"
<< std::endl;
}
else if (what == YanCaoAndJiaoShui) {
std::cout << "我是吸烟者【2】号,我拥有【纸】,我现在从桌上拿走【烟草】和【胶水】,我开始吸烟了"
<< std::endl;
}
else {
std::cout << "我是吸烟者【1】号,我拥有【烟草】,我现在从桌上拿走【纸】和【胶水】,我开始吸烟了"
<< std::endl;
}
what = 0;
}
DWORD WINAPI Producer(LPVOID) {
while (1) {
num++; //要放入什么
num = num % 3;
if (num == 0) {
Put(YanCaoAndZhi); //放入烟草和纸
V(offerYanCaoAndZhi); //唤醒拥有胶水的吸烟者
}
else if (num == 1) {
Put(YanCaoAndJiaoShui); //放入烟草和胶水
V(offerYanCaoAndJiaoShui); //唤醒拥有纸的吸烟者
}
else {
Put(ZhiAndJiaoShui); //放入纸和胶水
V(offerZhiAndJiaoShui); //唤醒拥有烟草的吸烟者
}
Sleep(1000);
P(finish); //是否有吸烟者吸完烟了?
}
return 0;
}
DWORD WINAPI Smoker1(LPVOID){ //拥有烟草的吸烟者
while (1) {
P(offerZhiAndJiaoShui); //供应者是否放好了纸和胶水
Smoke(); //拿走材料开始吸烟
Sleep(1000);
V(finish);
}
return 0;
}
DWORD WINAPI Smoker2(LPVOID) { //拥有纸的吸烟者
while (1) {
P(offerYanCaoAndJiaoShui); //供应者是否放好了烟草和胶水
Smoke(); //拿走材料开始吸烟
Sleep(1000);
V(finish);
}
return 0;
}
DWORD WINAPI Smoker3(LPVOID) { //拥有胶水的吸烟者
while (1) {
P(offerYanCaoAndZhi); //供应者是否放好了烟草和纸
Smoke(); //拿走 材料开始吸烟
Sleep(1000);
V(finish);
}
return 0;
}
运行效果:
文章来源地址https://www.toymoban.com/news/detail-422837.html
到了这里,关于C++实现经典同步问题(生产者消费者、读者写者、哲学家进餐、吸烟者问题)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!