线程通讯:
线程需要在两种情况下进行通讯
1、如果有多个线程访问共享资源而不能够使这个资源被破坏
2、当一个线程将某一个任务完成之后,通知另一个线程接着去完成下一个任务
线程的互斥访问分为
1、用户区间
2、内核对象(但内核对象并不是只能用来做线程互斥)
这里采用一个控制台应用程序进行说明:
//一、用户区间
//临界区
//临界区 1、定义一个临界区变量
//CRITICAL_SECTION g_cs;
//二、内核对象
//内核对象都可以处于已通知和未通知的两种状态,状态的切换是系统的规则来决定的
//在线程的运行过程中,线程内核对象处于未通知状态,当线程结束时,变为已通知(抢占)
//事件 基于内核对象的同步机制
//事件 1、事件的句柄的定义
//HANDLE g_hEvent;
//互斥对象 用来确保线程的单个资源的互斥访问权
//包含一个使用数量,一个线程id和一个递归的计数器
//而互斥对象的行为和临界区有些类似,只不过互斥属于内核,临界区属于用户
//id来标识系统中哪个线程拥有互斥对象,递归计数器用于表示这个线程拥有互斥对象的次数
//互斥对象的使用规则:如果线程id为0,表示无效id,没有线程拥有互斥对象,互斥对象发出信号
//如果id非0,证明有线程拥有互斥对象,不发出信号。
//互斥对象 1、互斥对象句柄的定义
//HANDLE g_hMutex;
//信号量 1、信号量对象句柄的定义
HANDLE g_hSemaphore;
int piao = 10;
void aa(LPVOID v)
{
while (1)
{//临界区 3、进入临界区
//EnterCriticalSection(&g_cs);
//事件 3、等待通知
//WaitForSingleObject(g_hEvent, INFINITE);
//互斥 3、等待通知
//WaitForSingleObject(g_hMutex, INFINITE);
//信号量 3、等待通知
WaitForSingleObject(g_hSemaphore, INFINITE);
if (piao > 0)
{
printf("第%d个车站抢到第%d张票\n", (int)v, piao);
piao--;
}
else{
//临界区 4、离开临界区
//LeaveCriticalSection(&g_cs);
//事件 4、设置事件通知
//SetEvent(g_hEvent);
//互斥 4、重置互斥内核
//ReleaseMutex(g_hMutex);
//信号量 4、重置信号量
//第二个参数重置当前的资源数量
ReleaseSemaphore(g_hSemaphore, 1, 0);
break;
}
//LeaveCriticalSection(&g_cs);
//SetEvent(g_hEvent);
//ReleaseMutex(g_hMutex);
ReleaseSemaphore(g_hSemaphore, 1, 0);
}
}
int _tmain(int argc, _TCHAR* argv[])
{
//临界区 2、临界区变量初始化
//InitializeCriticalSection(&g_cs);
//事件 2、事件的初始化
//第2个参数表示重置,自动事件(重置),手动事件(重置)false(人工重置)
//自动重置:表示事件得到通知,等待这个事件的线程只有一个线程变为可调度线程
//手动重置:表示事件已经得到通知,等待这个事件的所有线程都变为可调度线程
//g_hEvent = CreateEvent(nullptr,//安全属性
//false,//自动事件还是手动事件
//true,//事件的初始值(已通知)
//nullptr);//事件内核对象名称
//互斥 2、互斥的初始化
//第二个参数:如果为false,表示互斥对象拥有的id和计数器为0
//如果为true,表示互斥对象拥有的id为调用线程id,计数器++;
//g_hMutex = CreateMutex(nullptr, false, nullptr);
//信号量 2、信号量的初始化
//第2/3个参数是借助信号量的特点,给出1,初始化的数量为1,最大数也为1,如果线程占用了,初始数量为1
g_hSemaphore = CreateSemaphore(nullptr, 1, 1, nullptr);
int index = 1;
HANDLE handle1 = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)aa, (LPVOID)index, 0, 0);
index++;
HANDLE handle2 = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)aa, (LPVOID)index, 0, 0);
index++;
HANDLE handle3 = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)aa, (LPVOID)index, 0, 0);
index++;
HANDLE handle4 = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)aa, (LPVOID)index, 0, 0);
while (1)
{
//临界区 3、进入临界区
//EnterCriticalSection(&g_cs);
//内核和用户区间是有区别的,内核会自动切换状态不需要手动去设置
//事件 3、等待通知
//WaitForSingleObject(g_hEvent, INFINITE);
//互斥 3、等待通知
//WaitForSingleObject(g_hMutex, INFINITE);
//信号量 3、等待通知
WaitForSingleObject(g_hSemaphore, INFINITE);
if (piao > 0)
{
printf("火车站抢到第%d张票\n", piao);
piao--;
}
else
{
//临界区 4、离开临界区
//LeaveCriticalSection(&g_cs);
//事件 4、设置事件通知
//SetEvent(g_hEvent);
//互斥 4、重置互斥内核
//ReleaseMutex(g_hMutex);
//信号量 4、重置信号量
//第2个参数表示重置当前的资源的数量
ReleaseSemaphore(g_hSemaphore, 1, 0);
break;
}
//LeaveCriticalSection(&g_cs);
//事件 设置事件的通知
//SetEvent(g_hEvent);
//互斥 4、重置互斥内核
//ReleaseMutex(g_hMutex);
//信号量 4、重置信号量
//第2个参数表示重置当前资源的数量
ReleaseSemaphore(g_hSemaphore, 1, 0);
}
//临界区 5、释放临界区
//DeleteCriticalSection(&g_cs);
//事件 关闭事件
//CloseHandle(g_hEvent);
//互斥 5、关闭互斥
//CloseHandle(g_hMutex);
//信号量 5、关闭信号量
CloseHandle(g_hSemaphore);
DWORD t1, t2, t3, t4;
while (1)
{
GetExitCodeThread(handle1, &t1);//得到线程的退出码
GetExitCodeThread(handle2, &t2);
GetExitCodeThread(handle3, &t3);
GetExitCodeThread(handle4, &t4);
//判断线程退出码如果是STILL_ACTIVE,表示线程仍然活跃,非STILL_ACTIVE,表示线程死亡
if (t1 != STILL_ACTIVE&&t2 != STILL_ACTIVE&&t3 != STILL_ACTIVE&&t4 != STILL_ACTIVE)
{
break;
}
}
TerminateThread(handle1, 0);//终止进程
TerminateThread(handle2, 0);
TerminateThread(handle3, 0);
TerminateThread(handle4, 0);
getchar();
return 0;
}