内核对象——事件内核对象:
事件内核对象(1)
1:线程的同步:
#include <cstdio>
#include <tchar.h>
#include <windows.h>
#include <process.h>
int *g_pNum = nullptr;
bool g_Using = false;
void Entry()//原子操作,进入锁
{
while (InterlockedExchange((long*)&g_Using, true) == true)
{
Sleep(0);
}
}
void Leave()//原子操作,离开锁。
{
InterlockedExchange((long*)&g_Using, false);
}
void ThreadPrintf(TCHAR* strText)
{
Entry();
_tprintf(strText);//非线程安全的,需要加锁。
Leave();
}
unsigned __stdcall StartThread(void* lParam)
{
ThreadPrintf(TEXT("Enter StartThread\r\n"));
g_pNum = new int(0);
Sleep(1000);
ThreadPrintf(TEXT("End StartThread\r\n"));
return 0;
}
unsigned __stdcall EndThread(void* lParam)
{
ThreadPrintf(TEXT("Enter EndThread\r\n"));
HANDLE hStart = lParam;
ThreadPrintf(TEXT("Waiting EndThread\r\n"));
WaitForSingleObject(hStart, INFINITE); //会使EndThread处于不可调度,节约资源。
delete g_pNum;
ThreadPrintf(TEXT("End EndThread\r\n"));
return 0;
}
int main()
{
HANDLE hStart = (HANDLE)_beginthreadex(nullptr, 0, StartThread, nullptr, 0, nullptr);
// Sleep(1000);//用于控制先后顺序
HANDLE hEnd = (HANDLE)_beginthreadex(nullptr, 0, EndThread, hStart, 0, nullptr);
WaitForSingleObject(hEnd, INFINITE);
return 0;
}
2:事件内核对象也是一种用于线程进程同步的内核对象,它可以自动重置也可以手动重置。最为重要的参数是Signal参数,事件内核对象创建的时候可以使用那个CreateEvent。
创建事件内核对象的时候,会设置重置的方式,FALSE为自动重置,TRUE为手动重置。
事件内核对象(2)——自动及手动及信号及No信号:
1:很显然,Thread和Process的内核对象并不是专门拿来做同步的,只是里面有Signal这个成员了,可以对此进行Wait的操作,事件内核对象是一个较为特殊的内核对象,他并非一个实体,里面也有使用计数,Signal状态,我们可以自动或者手动设置状态。
2:使用CreateEvent,CreateEventEx(XP下无法使用)。
CreateEventW(
_In_opt_ LPSECURITY_ATTRIBUTES lpEventAttributes, //安全指针,一般传nullptr
_In_ BOOL bManualReset, //是否为人为置位。
_In_ BOOL bInitialState, //初始化状态。TRUER为有信号状态
_In_opt_ LPCWSTR lpName //事件内核对象名称。
);
3:其他进程仅能通过事件内核对象的名称来访问这个事件内核对象,如果事件内核对象内有设置名称就为匿名事件内核对象,这样的内核对象只能在当前进程里面使用。
#include <windows.h>
int main()
{
HANDLE hEvent = CreateEvent(nullptr, TRUE, TRUE, nullptr);//此事件内核对象只能在本进程里面使用
WaitForSingleObject(hEvent, INFINITE);//会等待一个内核对象变为Signal状态。
ResetEvent(hEvent);// 会使这个内核对象变为no Signal状态。
WaitForSingleObject(hEvent, INFINITE);//手动重置,不Reset就会直接通过,Reset就会阻塞在这里
//如果为自动重置,那么WaitForSingleObject就会改变内核对象里面的Signal状态,下一次WaitForSingleObject就会阻塞。
CloseHandle(hEvent);
return 0;
}
4:自动重置状态会导致WaitForSingleObject会改变事件内核对象里面的Signal状态,如果为手动重置,那么在不掉用ResetEvent的情况下,内核对象会一直处于有信号状态,WaitForSingleObject也不可以改变其Signal状态。
事件内核对象(3)——作业:
1:CreateEventEx:参数含义与CreateEvent有所不同。
CreateEventExW(
_In_opt_ LPSECURITY_ATTRIBUTES lpEventAttributes, //权限访问符
_In_opt_ LPCWSTR lpName, //内核对象名称
_In_ DWORD dwFlags, //CREATE_EVENT_INITIAL_SET或者CREATE_EVENT_MANUAL_RESET。代表有信号状态,手动的。不传递这个参数,就为noSignal的自动的。
_In_ DWORD dwDesiredAccess //权限设置。
);
2:CreateEvent是默认全权限的,可以做任何事情,而CreateEventEx可以设置里面的权限。主要用于设置对Event操作的权限。也有读权限,写权限等:
读权限:只能来WaitForSignalObject,写权限:可以Set和Reset内核对象。SetEvent可以使之变为有信号状态,ResetEvent可以使之变为无信号状态。
事件内核对象(4)——练习:
1:我们在word里面编辑的时候,会发现其有字数统计(会有延后),拼写检查等等,输入网址他会自动设置为连接(渲染),事实上,他是使用的多线程来实现的。
void CEventDemoDlg::OnSetfocusEditText()
{
// TODO: 在此添加控件通知处理程序代码
if (m_hThreadGetCount == INVALID_HANDLE_VALUE)
{
m_hThreadGetCount = (HANDLE)_beginthreadex(nullptr, 0, ThreadGetCount, this, 0, nullptr);
}
if (m_hIsUrl == INVALID_HANDLE_VALUE)
{
m_hIsUrl = (HANDLE)_beginthreadex(nullptr, 0, ThreadIsUrl, this, 0, nullptr);
}
SINGLE_LIST_ENTRY hEventSignal;
SetEvent(m_hEvent);//Event变为有信号
}
void CEventDemoDlg::OnKillfocusEditText()
{
// TODO: 在此添加控件通知处理程序代码
ResetEvent(m_hEvent);//无信号3
}
unsigned __stdcall CEventDemoDlg::ThreadGetCount(void* lParam)
{
CEventDemoDlg* pThis = (CEventDemoDlg*)lParam;
static CString strText;
size_t nCount = 0;
while (TRUE)
{
WaitForSingleObject(pThis->m_hEvent, INFINITE);
pThis->GetDlgItemText(IDC_EDIT_TEXT, strText);
nCount = strText.GetLength();
pThis->SetDlgItemInt(IDC_STATIC_COUNT, nCount);
Sleep(50);
}
return 0;
}
unsigned __stdcall CEventDemoDlg::ThreadIsUrl(void* lParam)
{
CEventDemoDlg* pThis = (CEventDemoDlg*)lParam;
WaitForSingleObject(pThis->m_hEvent, INFINITE);
return 0;
}
2:上面是使用事件内核对象和多线程统计字数的一个例子。