C++ 多线程学习总结

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/hust_bochu_xuchao/article/details/53080198

本文主要参考以下链接,感谢前辈的分享。

秒杀多线程    多线程总结  

先来一个简单的例子,展示如何创建一个线程

#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)
{
   case WAIT_OBJECT_0:
      // hProcess所代表的进程在5秒内结束
       break;

   case WAIT_TIMEOUT:
      // 等待时间超过5秒
       break;

   case WAIT_FAILED:
      // 函数调用失败,比如传递了一个无效的句柄
       break;
}

还可以使用WaitForMulitpleObjects函数来等待多个内核对象变为已通知状态:

DWORD WaitForMultipleObjects(

   DWORD dwCount,     //等待的内核对象个数
   CONST HANDLE* phObjects,     //一个存放被等待的内核对象句柄的数组
   BOOL bWaitAll,     //是否等到所有内核对象为已通知状态后才返回
   DWORD dwMilliseconds);     //等待时间
  该函数的第一个参数指明等待的内核对象的个数,可以是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()就可以完成清理与销毁了。


阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页