一.什么是线程同步
首先,我们要明确线程同步的概念,所谓线程同步
就是让线程执行顺序是有序的.因为互斥可以保证A访问的时候B访问不了.但有可能A会访问多次.线程无序.此时同步的意思就是 我就想让A执行完在执行B.
这个就是同步.
二.Win32API 事件
事件,是Win32中管理线程同步所使用的API。
HANDLE CreateEventA( LPSECURITY_ATTRIBUTES lpEventAttributes, // SD安全属性 BOOL bManualReset, //通知类型 BOOL bInitialState, //初始值有信号还是无信号.false无信号 LPCSTR lpName //全局名字 ); 返回事件句柄
第三个参数我们很好理解. 有信号还是无信号. false为无信号. true为有信号. 这样Wait函数根据有无信号就可以进行线程是否执行了.
主要是第二个参数. 通知类型.这个比较复杂.
通知类型的意思就是指. 如果我们按照以前.我们使用了wait函数. 那么有信号会变为无信号.除非释放才会继续有信号执行.
而现在的通知类型如果为TRUE. 那么wait函数执行的时候.你有信号我不会自动变为无信号了.除非你手动自己更改.
如果通知类型为FALSE 那么则自动设置.有信号使用wait函数接受了.那么就变成无信号了.
BOOL SetEvent(HANDLE hEvent); //设置信号状态,转为已通知状态
三.利用Event实现生产者消费者问题
下面给出源码,解释已经非常详细,不再进行过多讲解
#include "stdafx.h" #include <Windows.h> #include "Mycpp.h" //这是本人添加的库文件,可忽略 HANDLE g_hSet; //将句柄声明为全局变量,方便使用 HANDLE g_hClear; int g_Max=10; //给定变量初始值 int g_Num=0; DWORD WINAPI ThreadProc1(LPVOID lpParameter) //生产者线程函数 { for(int i =0;i<g_Max;i++) { WaitForSingleObject(g_hSet,INFINITE); //因为g_Set的初始状态为已通知,所以可以立即执行下面的代码,且状态会被改变为未通知 g_Num=1; DWORD id = GetCurrentThreadId(); //该API获取线程的ID printf("生产者%d将数据%d放入缓冲区 ",id,g_Num); SetEvent(g_hClear); //这里会将g_hClear的状态设置为已通知状态,即消费者线程函数的Wait接收到信号并执行 } return 0; } DWORD WINAPI ThreadProc2(LPVOID lpParameter) //消费者线程函数 { for(int i =0;i<g_Max;i++) { WaitForSingleObject(g_hClear,INFINITE); //执行以下代码,并将g_hClear的状态设置为未通知 g_Num=0; DWORD id = GetCurrentThreadId(); printf("消费者%d将数据%d放入缓冲区 ",id,g_Num); SetEvent(g_hSet); //再将g_hSet的状态设置为已通知,实现同步执行,即生产者生产,消费者立马消费 } return 0; } int main(int argc, char* argv[]) { HANDLE hThread[2]; //创建一个句柄数组 g_hSet = CreateEvent(NULL,FALSE,TRUE,NULL); //分别创建两个事件,其中g_hSet(生产者事件)初始状态设置为已通知状态,即Wait后可以立即执行 g_hClear = CreateEvent(NULL,FALSE,FALSE,NULL); hThread[0]= CreateThread(NULL,0,ThreadProc1,NULL,0,NULL); hThread[1]= CreateThread(NULL,0,ThreadProc2,NULL,0,NULL); WaitForMultipleObjects(2,hThread,TRUE,INFINITE); //当上述线程执行完毕后,执行以下代码 CloseHandle(hThread[0]); // 关闭句柄,销毁内核对象 CloseHandle(hThread[1]); CloseHandle(g_hSet); CloseHandle(g_hClear); return 0; }
执行后结果为:
至此,我们已经利用Event实现了线程同步问题