1.内核对象
1.什么是内核对象
前面我们知道了怎么判断是否为内核对象,即在创建对象时是否有LPSECURITY_ATTRIBUTES参数,起始我们创建的内核对象会给我们返还一个句柄,句柄就是内核对象的一个编号,因为操作系统不可能把真正的地址给我们,避免出现恶意破坏的风险
2.事件内核对象的创建
HANDLE g_hEvent = CreateEvent(NULL, TRUE, FALSE, "XYZ");
HANDLE g_hMutex = CreateMutex(NULL,FALSE, "XYZ");
3.内核对象的获取
以Open的方式我们可以跨进程获取对象
4.内核对象的销毁
当内核对象被其他进程引用时,内核对象会被延长声生命周期
5.内核对象的生命周期
实验:CloseHandle()/进程结束
进程一:
HANDLE g_hEvent = CreateEvent(NULL, TRUE, FALSE, "XYZ");
SetEvent(g_hEvent);
进程二:
HANDLE g_hEvent = OpenEvent(EVENT_ALL_ACCESS, FALSE, "XYZ");
WaitForSingleObject(g_hEvent, INFINITE);
实验一:内核对象的生命周期
进程一:
HANDLE g_hEvent = CreateEvent(NULL, TRUE, FALSE, "XYZ");
SetEvent(g_hEvent);
进程二:
HANDLE g_hEvent = OpenEvent(EVENT_ALL_ACCESS, FALSE, "XYZ");
WaitForSingleObject(g_hEvent, INFINITE);
当进程1创建了内核对象后,不销毁,此时进程2进行引用的话对象的生命周期就会被延长
当进程1创建了内核对象后,销毁的话,此时进程2就无法进行引用,对象的生命周期结束
当进程1创建了内核对象后,不进行销毁,此时进程2进行引用,进程1再进行销毁时,对象的生命周期依然存在
2.事件对象
1.事件对象的创建
第二个参数为TRUE时,表示需要手动将事件对象标记为未通知(即一直保持已通知状态),为FALSE时,表示WaitForSingleObject函数会自动将事件对象修改为未通知状态。
第三个参数为TRUE时,表示事件对象创建出来就是已通知状态,为FALSE时,表示创建出来就是未通知状态
2.事件状态的控制
BOOL SetEvent(HANDLE hEvent); //可以实现将事件状态修改为已通知
BOOL ResetEvent(HANDLE hEvent); //可以实现将事件状态修改为未通知
3.关闭事件对象的控制
CloseHandle(); //关闭句柄
4.线程控制实验:只读形式的线程控制
// xxxx.cpp : Defines the entry point for the application.
//
#include "stdafx.h"
#include <windows.h>
#include "resource.h"
HWND hEdit1;
HWND hEdit2;
HWND hEdit3;
HWND hEdit4;
HANDLE hThread1;
HANDLE hThread2;
HANDLE hThread3;
HANDLE g_hEvent;
DWORD WINAPI ThreadProc1(LPVOID lpParameter)
{
WaitForSingleObject(g_hEvent,-1);
TCHAR buffer[10];
memset(buffer,0,10);
GetWindowText(hEdit1,buffer,10);
SetWindowText(hEdit2,buffer);
return 0;
}
DWORD WINAPI ThreadProc2(LPVOID lpParameter)
{
WaitForSingleObject(g_hEvent,-1);
TCHAR buffer[10];
memset(buffer,0,10);
GetWindowText(hEdit1,buffer,10);
SetWindowText(hEdit3,buffer);
return 0;
}
DWORD WINAPI ThreadProc3(LPVOID lpParameter)
{
WaitForSingleObject(g_hEvent,-1);
TCHAR buffer[10];
memset(buffer,0,10);
GetWindowText(hEdit1,buffer,10);
SetWindowText(hEdit4,buffer);
return 0;
}
DWORD WINAPI ThreadProc_Main(LPVOID lpParameter)
{
g_hEvent=CreateEvent(NULL,true,false,NULL);
hThread1=CreateThread(NULL,0,ThreadProc1,NULL,0,NULL);
hThread2=CreateThread(NULL,0,ThreadProc2,NULL,0,NULL);
hThread3=CreateThread(NULL,0,ThreadProc3,NULL,0,NULL);
SetEvent(g_hEvent);
CloseHandle(hThread1);
CloseHandle(hThread2);
CloseHandle(hThread3);
CloseHandle(g_hEvent);
return 0;
}
BOOL CALLBACK MainDlgProc(HWND hDlg,UINT uMsg,WPARAM wParam,LPARAM lParam)
{
switch(uMsg)
{
case WM_CLOSE:
{
EndDialog(hDlg,0);
break;
}
case WM_INITDIALOG:
{
hEdit1 = GetDlgItem(hDlg,IDC_EDIT1);
SetWindowText(hEdit1,"1000");
hEdit2 = GetDlgItem(hDlg,IDC_EDIT2);
SetWindowText(hEdit2,"0");
hEdit3 = GetDlgItem(hDlg,IDC_EDIT3);
SetWindowText(hEdit3,"0");
hEdit4 = GetDlgItem(hDlg,IDC_EDIT4);
SetWindowText(hEdit4,"0");
break;
}
case WM_COMMAND:
{
switch (LOWORD (wParam))
{
case IDC_BUTTON1:
{
HANDLE hThread_Main=CreateThread(NULL,0,ThreadProc_Main,NULL,0,NULL);
CloseHandle(hThread_Main);
return TRUE;
}
}
}
break;
}
return FALSE;
}
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
DialogBox(hInstance,MAKEINTRESOURCE(IDD_DIALOG_MAIN),NULL,MainDlgProc);
return 0;
}
注意:这里能够实现三个线程同时读取全局变量是因为在创建事件对象时,我们第二个参数是true,因此在使用的时候,如果我们不手动修改事件对象的状态的话,那么事件对象的状态就一直是已通知状态,那么其他线程就能Wait到
3.线程同步
什么是线程同步?
线程同步是指在多线程编程中,为了保证多个线程按照某种特定的方式正确、有序地执行,需要进行线程间的协作与同步。在多线程编程中,当多个线程共享同一份资源时,由于线程的执行顺序是不确定的,因此会存在一些并发问题,如死锁、竞态条件、资源争用等问题。为了避免这些问题,需要对线程进行同步。线程同步实际上就是通过线程之间的协作,使得线程能够按照一定的顺序来访问共享资源,从而避免并发问题的发生。
参见:线程同步
事件对象实现线程同步
#include "stdafx.h"
#include <windows.h>
#include <stdio.h>
//事件和临界区
HANDLE g_hSet, g_hClear;
int g_Max = 10;
int g_Number = 0;
//生产者线程函数
DWORD WINAPI ThreadProduct(LPVOID pM)
{
for (int i = 0; i < g_Max; i++)
{
WaitForSingleObject(g_hSet, INFINITE);
g_Number = 1;
DWORD id = GetCurrentThreadId();
printf("生产者%d将数据%d放入缓冲区\n",id, g_Number);
SetEvent(g_hClear);
}
return 0;
}
//消费者线程函数
DWORD WINAPI ThreadConsumer(LPVOID pM)
{
for (int i = 0; i < g_Max; i++)
{
WaitForSingleObject(g_hClear, INFINITE);
g_Number = 0;
DWORD id = GetCurrentThreadId();
printf("----消费者%d将数据%d放入缓冲区\n",id, g_Number);
SetEvent(g_hSet);
}
return 0;
}
int main(int argc, char* argv[])
{
HANDLE hThread[2];
g_hSet = CreateEvent(NULL, FALSE, TRUE, NULL);
g_hClear = CreateEvent(NULL, FALSE, FALSE, NULL);
hThread[0] = ::CreateThread(NULL, 0, ThreadProduct, NULL, 0, NULL);
hThread[1] = ::CreateThread(NULL, 0, ThreadConsumer, NULL, 0, NULL);
WaitForMultipleObjects(2, hThread, TRUE, INFINITE);
CloseHandle(hThread[0]);
CloseHandle(hThread[1]);
//销毁
CloseHandle(g_hSet);
CloseHandle(g_hClear);
return 0;
}