参考:https://blog.youkuaiyun.com/u011394598/article/details/82981399
msdn : https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-createeventa
m_hThreadStarted = CreateEvent(0,0,0,0);
SetEvent(m_hThreadStarted);
HANDLE CreateEventA(
LPSECURITY_ATTRIBUTES lpEventAttributes,
BOOL bManualReset,
BOOL bInitialState,
LPCSTR lpName
);
bManualReset
如果此参数为TRUE,则该函数将创建一个手动重置事件对象,该对象需要使用 ResetEvent函数将事件状态设置为非信号状态。如果此参数为FALSE,则该函数将创建一个自动重置事件对象,并且在释放单个等待线程之后,系统会自动将事件状态重置为无信号。
bInitialState
如果此参数为TRUE,则表示事件对象的初始状态;否则,将通知事件对象。否则,它是无信号的。
lpName
事件对象的名称。名称不得超过 MAX_PATH个字符。名称比较区分大小写。
如果lpName与现有命名事件对象的名称匹配,则此函数请求EVENT_ALL_ACCESS访问权限。在这种情况下, bManualReset和bInitialState参数将被忽略,因为它们已由创建过程设置。如果 lpEventAttributes参数不为NULL,则它确定是否可以继承该句柄,但是将忽略其安全性描述符成员。
如果lpName为NULL,则创建事件对象时不使用名称。
如果lpName与相同名称空间中的另一种对象的名称匹配(例如现有的信号量,互斥量,可等待的计时器,作业或文件映射对象),则该函数将失败,并且 GetLastError函数将返回 ERROR_INVALID_HANDLE。发生这种情况是因为这些对象共享相同的名称空间。
#include <windows.h>
#include <stdio.h>
#define THREADCOUNT 4
HANDLE ghWriteEvent;
HANDLE ghThreads[THREADCOUNT];
DWORD WINAPI ThreadProc(LPVOID);
void CreateEventsAndThreads(void)
{
int i;
DWORD dwThreadID;
// Create a manual-reset event object. The write thread sets this
// object to the signaled state when it finishes writing to a
// shared buffer.
ghWriteEvent = CreateEvent(
NULL, // default security attributes
TRUE, // manual-reset event
FALSE, // initial state is nonsignaled
TEXT("WriteEvent") // object name
);
if (ghWriteEvent == NULL)
{
printf("CreateEvent failed (%d)\n", GetLastError());
return;
}
// Create multiple threads to read from the buffer.
for(i = 0; i < THREADCOUNT; i++)
{
// TODO: More complex scenarios may require use of a parameter
// to the thread procedure, such as an event per thread to
// be used for synchronization.
ghThreads[i] = CreateThread(
NULL, // default security
0, // default stack size
ThreadProc, // name of the thread function
NULL, // no thread parameters
0, // default startup flags
&dwThreadID);
if (ghThreads[i] == NULL)
{
printf("CreateThread failed (%d)\n", GetLastError());
return;
}
}
}
void WriteToBuffer(VOID)
{
// TODO: Write to the shared buffer.
printf("Main thread writing to the shared buffer...\n");
// Set ghWriteEvent to signaled
if (! SetEvent(ghWriteEvent) )
{
printf("SetEvent failed (%d)\n", GetLastError());
return;
}
}
void CloseEvents()
{
// Close all event handles (currently, only one global handle).
CloseHandle(ghWriteEvent);
}
int main( void )
{
DWORD dwWaitResult;
// TODO: Create the shared buffer
// Create events and THREADCOUNT threads to read from the buffer
CreateEventsAndThreads();
// At this point, the reader threads have started and are most
// likely waiting for the global event to be signaled. However,
// it is safe to write to the buffer because the event is a
// manual-reset event.
WriteToBuffer();
printf("Main thread waiting for threads to exit...\n");
// The handle for each thread is signaled when the thread is
// terminated.
dwWaitResult = WaitForMultipleObjects(
THREADCOUNT, // number of handles in array
ghThreads, // array of thread handles
TRUE, // wait until all are signaled
INFINITE);
switch (dwWaitResult)
{
// All thread objects were signaled
case WAIT_OBJECT_0:
printf("All threads ended, cleaning up for application exit...\n");
break;
// An error occurred
default:
printf("WaitForMultipleObjects failed (%d)\n", GetLastError());
return 1;
}
// Close the events to clean up
CloseEvents();
return 0;
}
DWORD WINAPI ThreadProc(LPVOID lpParam)
{
// lpParam not used in this example.
UNREFERENCED_PARAMETER(lpParam);
DWORD dwWaitResult;
printf("Thread %d waiting for write event...\n", GetCurrentThreadId());
dwWaitResult = WaitForSingleObject(
ghWriteEvent, // event handle
INFINITE); // indefinite wait
switch (dwWaitResult)
{
// Event object was signaled
case WAIT_OBJECT_0:
//
// TODO: Read from the shared buffer
//
printf("Thread %d reading from buffer\n",
GetCurrentThreadId());
break;
// An error occurred
default:
printf("Wait error (%d)\n", GetLastError());
return 0;
}
// Now that we are done reading the buffer, we could use another
// event to signal that this thread is no longer reading. This
// example simply uses the thread handle for synchronization (the
// handle is signaled when the thread terminates.)
printf("Thread %d exiting\n", GetCurrentThreadId());
return 1;
}
下面原文链接找不到了
WaitForSingleObject和WaitForMultipleObjects是两个常用的挂起线程的函数,这里再介绍几个其它的函数:
(1)Sleep函数:
VOID Sleep(DWORD cMilliseconds);
说明:该函数将使线程自我挂起cMilliseconds毫秒。Sleep使线程自动放弃了时间片的剩余部分。如果cMilliseconds是0,该函数也会使CPU停止执行当前线程而分配给下一个等待执行的线程。[@more@]
(2)WaitForInputIdle函数:
DWORD WaitForInputIdle(HANDLE hProcess,DWORD dwTimeOut);
说明:该函数一直等到“由hProcess标识的进程的应用程序中第一个窗口的线程不再有输入”时为止。
(3)MsgWaitForMultipleObjects函数:
DWORD MsgWaitForMultipleObjects(DWORD dwCount,LPHANDLE lpHandles,BOOL bWaitAll,DWORD dwMilliseconds,DWORD dwWakeMask);
参数说明:dwWakeMask该参数可以被应用程序来确定它是否应该醒来处理某些特定类型的消息。例如:如果线程想要挂起自己,直到输入队列中出现了任何按键消息或鼠标消息,它可以如下这样实现:
MsgWaitForMultipleObjects(0,NULL,TRUE,INFINITE,QS_KEY|QS_MOUSE);
如果在等待某一对象变成有信号状态的同时又允许用户终止这一等待,那么该函数将会很有用。
(4)WaitForDebugEvent函数:
首先要讲一些调试相关的内容:在调试器运行时,它会依附于被调试者。调试器只是在那里等待系统将被调试者有关的事件通知给它。调试器就是通过使用下面的函数WaitForDebugEvent来等待这些事件发生的,
BOOL WaitForDebugEvent(LPDEBUG_EVENT lpde,DWORD dwTimeOut);
当调试器调用完WaitForDebugEvent之后,它的线程就被挂起,系统在唤醒线程前,添写了lpde结构。
(5)SingleObjectAndWait函数:
BOOL SingleObjectAndWait(HANDLE hObjectToSignal,HANDLE hObjectToWaitOn,DWORD dwMilliseconds,BOOL bAlertable);
参数说明:hObjectToSignal必须为互斥量、信号量或事件。hObjectToWaitOn是下面内核对象中的一个:互斥量、信号量、事件、计时器、进程、线程、文件改变通知或控制台输入。bAlertable表示线程是否能够在等待的时候处理任意队列化的异步过程调用。
该函数有一点需要注意的地方是,它的返回值实际上并不是布尔值,而是下面所示值之一:WAIT_OBJECT_0、WAIT_TIMEOUT、WAIT_FAILED、WAIT_ABANDONED、WAIT_IO_COMPLETION。
(6)InterLocked类函数:
共有InterLockedIncrement、InterLockedDecrement、InterLockedExchange、InterLockedExchangeAdd、InterLockedCompareExchange几种函数。
L0NG InterLockedIncrement(PLONG plValue);
LONG InterLockedDecrement(PLONG plValue);
LONG InterLockedExchange(PLONG plTarget,LONG lValue);
LONG InterLockedExchangeAdd(PLONG plAddend,LONG lIncrement);
PVOID InterLockedCompareExchange(PVOID * Destination,PVOID Exchange,PVOID Comperand);
这些函数都是用来修改长整数的。
InterLockedIncrement会将地址plValue处的变量值加1,InterLockedDecrement会将地址plValue处的变量值减1。这两个函数并不返回新值,而是返回新值与0的比较结果。返回值几乎从来不是变量的实际值。
InterLockedExchange用lValue的值改变plTarget地址处的变量值。
InterLockedExchangeAdd可以增加或减少指定的值。
InterLockedCompareExchange比较变量的当前值(由Destination)和Comperand的值。如果值相同,参数Destination就被改成Exchange的值,如果不同Destination的值就不被修改。举例:
LONG lValue = 5;
LONG lSomeValue = (LONG)InterLockedCompareExchange((PVOID)&lValue,(PVOID)500,(PVOID)5);
//在执行完InterLockedCompareExchange函数之后,因为Destination的值与Comperand的值相同,所以Destination的值被改成500。也就是本例中lValue的值被改成了500。
DWORD WINAPI ThreadProc1(LPVOID lpParameter);
HANDLE hand = CreateThread(NULL,0,ThreadProc1,this,0,NULL);
DWORD dwWart = WartForSingleObject(m_hThreadStarted, INFINITE);
ASSERT(dwWart == WAIT_OBJECT_0);
CloseHandle(m_hThreadStarted);
M_hThreadStared = INVALID_HANDLE_VALUE;
内核对象
令牌对象、事件对象、文件对象、文件映射对象、进程对象、信号量对象、线程对象、互斥量对象、管道对象、进程对象、线程对象、可等待的计时器对象、线程池工厂对象等。
调用CreateFileMapping创建对应于Section对象的文件映像。
CreateThread()
CreaeteFile()
CreateFileMapping()
CreateSemaphore()
使用完核心对象需要关闭相应句柄:CloseHandle()
当进程、线程结束时,线程对象处于激发状态;
当事件处于设置状态时,该对象处于激发状态;
当互斥量不被任何对象拥有时,该对象处于激发状态;
waitforsignalobject()结束阻塞状态。
信号量的规则:
当信号量的资源计数大于0时,该对象处于激发状态;
当信号量的资源计数等于0时,该对象处于未激发状态;
系统绝对不会让当前的资源计数为负数;
当前资源计数绝对不会最大资源计数。