- 环境:windows
问题描述:
一组生产者进程和一组消费者进程共享一个初始为空、大小为〃的缓冲区,只有缓冲区没满时,生产者才能把消息放入缓冲区,否则必须等待;只有缓冲区不空时,消费者才能从中取出消息,否则必须等待。由于缓冲区是临界资源,它只允许一个生产者放入消息,或一个消费者从中取出消息。
代码:
#include#include #include 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#include #include 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#include #include 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#include #include 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#include #include 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#include #include 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; }
运行效果:



