本文主要参考以下链接,感谢前辈的分享。
先来一个简单的例子,展示如何创建一个线程
#include <windows.h>
#include <process.h>
#include <iostream>
using namespace std;
unsigned Counter;
unsigned __stdcall SecondThreadFunc(PVOID pArguments)
{
printf("In second thread...\n");
while (Counter < 1000000)
Counter++;
printf("done\n");
_endthreadex(0);//如果把 printf("done\n");放在此段代码之后,则不会执行
return 0;
}
int main()
{
HANDLE hThread;
unsigned threadID;
// Create the second thread.
hThread = (HANDLE)_beginthreadex(NULL, 0, &SecondThreadFunc, NULL, 0, &threadID);//创建线程函数
WaitForSingleObject(hThread, INFINITE);//注意这个函数的使用
printf("Counter should be 1000000; it is-> %d\n", Counter);
cout << threadID << endl;
// Destroy the thread object.
CloseHandle(hThread);//个人感觉仅仅关闭了句柄,并没有关闭线程资源,也即线程还在跑
system("pause");
return 0;
}
WaitForSingleObject 函数介绍
WaitForSingleObject(hThread, INFINITE); 等待hThread这个线程发出信号,才会执行接下来的代码。INFINITE表示无期限等待,直到hThread线程发出信号。
WaitForSingleObject(hThread, 10); 表示等待10ms,10ms内发出信号,则返回WAIT_OBJECT_0,超过时间则返回WAIT_TIMEOUT。如果该函数失败,返回WAIT_FAILED。
可以通过下面的代码来判断:
DWORD dw = WaitForSingleObject(hProcess, 5000); //等待一个进程结束
switch (dw)
{
}
DWORD WaitForMultipleObjects(
DWORD dwCount,
该函数的第一个参数指明等待的内核对象的个数,可以是0到MAXIMUM_WAIT_OBJECTS(64)中的一个值。phObjects参数是一个存放等待的内核对象句柄的数组。bWaitAll参数如果为TRUE,则只有当等待的所有内核对象为已通知状态时函数才返回,如果为FALSE,则只要一个内核对象为已通知状态,则该函数返回。第四个参数和WaitForSingleObject中的dwMilliseconds参数类似。
临界区和事件 解决线程同步和互斥问题 的例子
#include<iostream>
#include <process.h>
#include <windows.h>
using namespace std;
long g_nNum;
unsigned int __stdcall Fun(PVOID pPM);
const int THREAD_NUM = 10;
//事件与关键段
HANDLE g_hThreadEvent;
CRITICAL_SECTION g_csThreadCode;
int main()
{
cout << " 经典线程同步 事件Event\n" << endl;
//初始化事件和关键段 自动置位,初始无触发的匿名事件
g_hThreadEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
InitializeCriticalSection(&g_csThreadCode);
HANDLE handle[THREAD_NUM];
g_nNum = 0;
int i = 0;
while (i < THREAD_NUM)
{
handle[i] = (HANDLE)_beginthreadex(NULL, 0, Fun, &i, 0, NULL);
WaitForSingleObject(g_hThreadEvent, INFINITE); //等待事件被触发
i++;
}
WaitForMultipleObjects(THREAD_NUM, handle, TRUE, INFINITE);
//销毁事件和关键段
CloseHandle(g_hThreadEvent);
DeleteCriticalSection(&g_csThreadCode);
system("pause");
return 0;
}
unsigned int __stdcall Fun(PVOID pPM)
{
int nThreadNum = *(int *)pPM;
SetEvent(g_hThreadEvent); //触发事件
Sleep(50);//some work should to do
EnterCriticalSection(&g_csThreadCode);
g_nNum++;
Sleep(0);//some work should to do
cout << "线程编号为:" << nThreadNum << "全局资源为:" << g_nNum << endl;
LeaveCriticalSection(&g_csThreadCode);
return 0;
}
关键段CRITICAL_SECTION一共就六个函数,使用很是方便。如下所示为常用的四个函数
函数功能:初始化
函数原型:void InitializeCriticalSection(LPCRITICAL_SECTIONlpCriticalSection);
函数说明:定义关键段变量后必须先初始化。
函数功能:销毁
函数原型:void DeleteCriticalSection(LPCRITICAL_SECTIONlpCriticalSection);
函数说明:用完之后记得销毁。
函数功能:进入关键区域
函数原型:void EnterCriticalSection(LPCRITICAL_SECTIONlpCriticalSection);
函数说明:系统保证各线程互斥的进入关键区域。
函数功能:离开关关键区域
函数原型:void LeaveCriticalSection(LPCRITICAL_SECTIONlpCriticalSection);
事件Event实际上是个内核对象,它的使用非常方便。下面列出一些常用的函数。(CreateEvent,SetEvent,ResetEvent)
CreateEvent
函数功能:创建事件
函数原型:
HANDLECreateEvent(
LPSECURITY_ATTRIBUTESlpEventAttributes,
BOOLbManualReset,
BOOLbInitialState,
LPCTSTRlpName
);
函数说明:
第一个参数表示安全控制,一般直接传入NULL。
第二个参数确定事件是手动置位还是自动置位,传入TRUE表示手动置位,传入FALSE表示自动置位。如果为自动置位,则对该事件调用WaitForSingleObject()后会自动调用ResetEvent()使事件变成未触发状态。打个小小比方,手动置位事件相当于教室门,教室门一旦打开(被触发),所以有人都可以进入直到老师去关上教室门(事件变成未触发)。自动置位事件就相当于医院里拍X光的房间门,门打开后只能进入一个人,这个人进去后会将门关上,其它人不能进入除非门重新被打开(事件重新被触发)。
第三个参数表示事件的初始状态,传入TRUR表示已触发。
第四个参数表示事件的名称,传入NULL表示匿名事件。
SetEvent
函数功能:触发事件
函数原型:BOOLSetEvent(HANDLEhEvent);
函数说明:每次触发后,必有一个或多个处于等待状态下的线程变成可调度状态。
ResetEvent
函数功能:将事件设为末触发
函数原型:BOOLResetEvent(HANDLEhEvent);
最后一个事件的清理与销毁
由于事件是内核对象,因此使用CloseHandle()就可以完成清理与销毁了。