多线程(五)多线程同步_Event事件
事件和互斥体同样属于内核同步对象,它和互斥体以及临界区在功能上有以下区别
前面的互斥体和临界区主要作用在于确保控制多个线程之间对共享资源访问,保证共享资源的完整性
事件主要作用是通知其它线程一个操作己经完成,可以接下来执行后面剩余操作了,确保多个线程之间的动作以指定的次序发生
例如:一个线程A用于初始化,一个线程B等待线程A初始化后执行后续操作。
当线程A初始化完成后设置线程B等待的事件为己通知状态,此时线程B从等待函数中得到事件己通知状态,就可以知道线程A己经执行完初始化操作,线程B开始执行后续操作
包含三个部份:
使用计数器: 所有内核对象都有这个属性
事件重置类型: 人工重置为TRUE,自动重置为FALSE
通知状态: 通知状态为TRUE,未通知状态为FALSE
注意:事件有一些特殊的地方,根据创建事件时使用自动重置还是人工重置,程序行为就有很大的差别
事件是人工重置时当得到通知,等待该事件的所有线程均变为可调度线程。而事件是自动重置时得到通知,等待该事件的线程中只有一个线程变为可调度线程
创建事件对象
HANDLE CreateEvent(PSECURITY_ATTRIBUTES psa, BOOL fManualReset, BOOL fInitialState, PCTSTR pszName);
1.psa 指向安全属性指针,一般为NULL
2.fManualReset 事件重置类型,人工重置为TRUE,自动重置为FALSE
3.fInitialState 初始化事件状态,通常为FALSE未通知状态
4.pszName 指定事件对象的名称
返回值:
成功返回新事件对象句柄,失败返回0
注意:如果己经有同名的事件对象存在,函数将返回己有的事件对象句柄,不会再创建新的事件对象
设置事件为己通知状态
BOOL SetEvent(HANDLE hEvent);
设置事件为己通知状态
BOOL ResetEvent(HANDLE hEvent);
编写一个Demo用于演示Event事件基本操作
功能介绍:
程序启动时创建三个线程,线程A B C. 先执行线程A,然后执行线程B,然后执行线程C
线程A执行初始化操作,给全局数组赋值大写字母A-E,然后通知线程B,线程B再把全局数组中所有字母改为小写a-e, 然后通知线C,线程C最后把全局数组中所有字母倒置
开始编写代码:
1. 创建个基于对话框的工程EventDemo
2. 添加一个编辑框用于显示信息,修改ID为IDC_EDIT_SHOWINFO, 修改属性为不可读.
3. 定义相关全局变量和线程函数前置声明
1 char g_szData[5] = {0}; 2 CEdit* g_pEdit; 3 HANDLE g_eventInit; //初始化事件 4 HANDLE g_eventBigToSmall; //大写转小写事件 5 HANDLE g_eventReverse; //倒置事件 6 7 //线程函数前置声明 8 DWORD WINAPI Thread_Init(LPVOID lpParam); 9 DWORD WINAPI Thread_BigToSmall(LPVOID lpParam); 10 DWORD WINAPI Thread_Reverse(LPVOID lpParam);
4. OnInitDialog中添加相应代码
1 BOOL CEventDemoDlg::OnInitDialog() 2 { 3 CDialogEx::OnInitDialog(); 4 5 // 设置此对话框的图标。当应用程序主窗口不是对话框时,框架将自动 6 // 执行此操作 7 SetIcon(m_hIcon, TRUE); // 设置大图标 8 SetIcon(m_hIcon, FALSE); // 设置小图标 9 10 //创建三个事件 11 g_eventInit = CreateEvent(NULL,FALSE,FALSE,NULL); 12 g_eventBigToSmall = CreateEvent(NULL,FALSE,FALSE,NULL); 13 g_eventReverse = CreateEvent(NULL,FALSE,FALSE,NULL); 14 //保存下编辑框指针供线程内部使用 15 g_pEdit = (CEdit*)GetDlgItem(IDC_EDIT_SHOWINFO); 16 //创建三个线程 17 CloseHandle(CreateThread(NULL,0,Thread_Init,NULL,0,NULL)); 18 CloseHandle(CreateThread(NULL,0,Thread_BigToSmall,NULL,0,NULL)); 19 CloseHandle(CreateThread(NULL,0,Thread_Reverse,NULL,0,NULL)); 20 21 //设置第一件事件为通知状态 22 SetEvent(g_eventInit); 23 24 return TRUE; // 除非将焦点设置到控件,否则返回 TRUE 25 }
5.三个线程代码
1 DWORD WINAPI Thread_Init(LPVOID lpParam) 2 { 3 WaitForSingleObject(g_eventInit,INFINITE); 4 //赋值五个大写字母A-E 5 g_szData[0] = 'A'; 6 g_szData[1] = 'B'; 7 g_szData[2] = 'C'; 8 g_szData[3] = 'D'; 9 g_szData[4] = 'E'; 10 //编辑框输出下相关信息 11 CString str; 12 g_pEdit->GetWindowText(str); 13 str += _T("线程_初始化:"); 14 for (int i=0; i<sizeof(g_szData); i++) 15 { 16 str += g_szData[i]; 17 } 18 str += _T("\r\n"); 19 g_pEdit->SetWindowText(str); 20 //设置为未通知状态 21 ResetEvent(g_eventInit); 22 //设置大写转小写事件为通知状态 23 SetEvent(g_eventBigToSmall); 24 return TRUE; 25 }
1 DWORD WINAPI Thread_BigToSmall(LPVOID lpParam) 2 { 3 WaitForSingleObject(g_eventBigToSmall,INFINITE); 4 //大写转小写 5 for (int i = 0; i<sizeof(g_szData); i++) 6 { 7 g_szData[i] = g_szData[i] + 32; 8 } 9 //编辑框输出下相关信息 10 CString str; 11 g_pEdit->GetWindowText(str); 12 str += _T("线程_大写转小写:"); 13 for (int i=0; i<sizeof(g_szData); i++) 14 { 15 str += g_szData[i]; 16 } 17 str += _T("\r\n"); 18 g_pEdit->SetWindowText(str); 19 //设置为未通知状态 20 ResetEvent(g_eventBigToSmall); 21 //设置倒置事件为通知状态 22 SetEvent(g_eventReverse); 23 return TRUE; 24 }
1 DWORD WINAPI Thread_Reverse(LPVOID lpParam) 2 { 3 WaitForSingleObject(g_eventReverse,INFINITE); 4 //倒置 5 int i = 0; 6 int j = sizeof(g_szData) - 1; 7 int temp = 0; 8 for (;i<j;i++,j--) 9 { 10 temp = g_szData[i]; 11 g_szData[i] = g_szData[j]; 12 g_szData[j] = temp; 13 } 14 //编辑框输出下相关信息 15 CString str; 16 g_pEdit->GetWindowText(str); 17 str += _T("线程_倒置:"); 18 for (i=0; i<sizeof(g_szData); i++) 19 { 20 str += g_szData[i]; 21 } 22 str += _T("\r\n"); 23 str += _T("程序执行完毕\r\n"); 24 g_pEdit->SetWindowText(str); 25 //设置为未通知状态 26 ResetEvent(g_eventReverse); 27 return TRUE; 28 }
6.DestroyWindow添加相应代码
1 BOOL CEventDemoDlg::DestroyWindow() 2 { 3 CloseHandle(g_eventInit); 4 CloseHandle(g_eventBigToSmall); 5 CloseHandle(g_eventReverse); 6 7 return CDialogEx::DestroyWindow(); 8 }