Window下C++的线程同步demo
今天考试自我感觉十分不理想,浏览了一下课程中就写的线程同步demo,提会了曾经的思维方式,发现我将约束(同步)抽象较高层次了,条件(顺序)约束需要往下分解才能直接用于实践指导!比如说:
- 资源个数——初始值为n。
- 开始活动的信号——初始值0/1。
- (以后来补充)
对于资源个数,其实可以通过以不同角度去定义资源来广义地扩大适用范围。
以后的补充
2020-12-31
大多数平台提供的同步机制中等待队列(阻塞队列)的次序默认是FIFO,且不能直接应用在需要通知并立即等待(即先V操作,再立刻P操作陷入等待事件)的可区分线程之间!
我的解决方案是自己实现信号量,下面是大致点与一些分析。
- 利用互斥锁实现可区分等待队列,且维护通知对象队列。
- 通知并等待通知对象处理完的线程按以下步骤执行:
①将自己插入等待队列。
②查看通知对象队列是否不为空,若不为空,则唤醒其进行处理。
③等待唯一性消息(由通知对象发出)。
该方案存在两个关键点——唯一性消息和如何等待!消息唯一是为了支持线程可区分,这一点至关重要!而怎样的等待可以避免忙等等损坏性能还能做到一定程度上的实时,是如何等待的关键点!
下面是我目前想到的唯一性消息的几种实例,但都存在一些瑕疵。
- 等待对象的自身状态。由等待对象在第一步时,将自身状态更新为“等待”,通知对象对其进行修改作为唯一性信号。
- 线程信息。可以将环境自带的线程操作看作唯一性消息,例如悬挂/取消悬挂、终止信号(C#的abort,可以通过try捕捉异常继续执行)等等。
这两个都存在问题。第一个,它的通用性很差,目前想到的弥补方式是该信号量实现要求等待对象实现状态检查、状态修改接口。第二个,就是一个经典的线程同步问题,存在等待对象的线程还没有悬挂自身,或者没进入try内,通知对象已经发出取消悬挂之类的信息了!弥补方式是,等待队列的互斥锁在插入自身之后不立刻释放,而是进入等待信号区域后释放;但这个只能用于终止信号的try捕捉类型的消息,类似悬挂这种一进入等待区域后就不能执行任何其他代码的方式就无法使用该弥补方式!
另一方面,如何等待更是一个性能方面的关键问题,主要目标是避免循环忙等现象(死等只有通知对象不存在的情况下出现,故不做考虑)。很遗憾,我目前只想到间断休眠等待而已!希望有大佬指点迷津!
这方面感觉是有些无解,忙等与实时彼此矛盾,至少我所了解OS提供的API无法解决这个矛盾!(OS层理论上可以清除这种矛盾)
第一次编辑代码展示
附上代码。一些简单的window API就不解释了。
#include <stdio.h>
#include <windows.h>
/*
功能:爸爸妈妈儿子女儿线程模拟
爸爸给苹果,女儿吃 (条件约束)
妈妈给橘子,儿子吃 (条件约束——儿子开始活动的信号)
盘子只能放下一个, (条件约束——盘子容量资源)
且不能同时放。 (互斥约束)
*/
//定义互斥锁和
HANDLE dishAccess;
HANDLE orangeNumber,AppleNumber,dishCapcity; //信号量,条件约束
//线程函数 dady
DWORD WINAPI dady(LPVOID lpParam) {
int Orage = (int)lpParam;
DWORD dwWrite;
for (int i = 0; i < Orage; i++) {
//进入临界区
//真正实践项目,应该检测返回值是否等待超时,infinite也是有限的,不检测在极端情况可能会出错!
WaitForSingleObject(dishAccess,INFINITE);
WaitForSingleObject(dishCapcity, INFINITE);
ReleaseSemaphore(orangeNumber,1,NULL);
printf("father puts one orange into dish\n", Orage);
//退出临界区
ReleaseMutex(dishAccess);
}
return 0;
}
//线程函数 mom
DWORD WINAPI mom(LPVOID lpParam) {
int apple = (int)lpParam;
DWORD dwWrite;
for (int i = 0; i < apple; i++) {
//进入临界区
WaitForSingleObject(dishAccess, INFINITE);
WaitForSingleObject(dishCapcity, INFINITE);
ReleaseSemaphore(AppleNumber, 1, NULL);
printf("monther puts one apple into dish\n", apple);
//退出临界区
ReleaseMutex(dishAccess);
}
return 0;
}
//线程函数 son
DWORD WINAPI son(LPVOID lpParam) {
int Capaticy = (int)lpParam; //儿子能吃多少
DWORD dwWrite;
for (int i = 0; i < Capaticy; i++) {
//等待橘子
WaitForSingleObject(orangeNumber, INFINITE);
printf("son eat the %d orange!\n",i+1);
ReleaseSemaphore(dishCapcity, 1, NULL);
}
return 0;
}
//线程函数 daughter
DWORD WINAPI daughter(LPVOID lpParam) {
int capacity = (int)lpParam;
DWORD dwWrite;
for (int i = 0; i < capacity; i++) {
//等待苹果
WaitForSingleObject(AppleNumber, INFINITE);
printf("daughter eat the %d apple!\n", i+1);
ReleaseSemaphore(dishCapcity, 1, NULL);
}
return 0;
}
int main() {
DWORD ThreadID;
HANDLE hThread[4];
DWORD (*threadFunction[])(LPVOID lpParam) = {dady,mom,son,daughter };
//初始化信号量
AppleNumber = CreateSemaphore(NULL, 0, 5, NULL);
orangeNumber = CreateSemaphore(NULL, 0, 5, NULL);
dishAccess = CreateMutex(NULL, FALSE, NULL);
dishCapcity = CreateSemaphore(NULL, 1, 1, NULL);
for (int i = 0; i < 4; i++) {
//创建线程,并调用Thread写文件
hThread[i] = CreateThread(NULL, 0, threadFunction[i], (LPVOID)(5), 0, &ThreadID);
ThreadID++;
printf("Thread #%d has been created successfully.\n", i + 1);
}
//等待所有进程结束
WaitForMultipleObjects(4, hThread, TRUE, INFINITE);
//删除临界区
return 0;
}