开发中引入多线程技术,能有效的利用现代计算机的多核资源,更高效。特别是在一些特别的开发中,例如订票系统,聊天系统......
但多线程开发要注意很多问题,例如多线程死锁,线程同步问题。其中线程同步问题尤其常见,因为各个线程之间可能需要共享一些全局变量,某个线程对于全局变量的修改可能会引起其他线程的异常。这是由于计算机的资源分配时按时间分片的,在某个临界区你可能了全局变量的值,但是可能接下来会由其他的线程修改掉。
本文介绍windows程序设计中线程同步的三种方式。
一、互斥对象(内核对象)
HANDLE CreateMutex( LPSECURITY_ATTRIBUTES lpMutexAttributes, // 安全参数设置,默认NULL为系统设置 BOOL bInitialOwner, //初始化所有者,true的默认创建这个互斥对象的线程获得其所有权 LPCTSTR lpName // 互斥对象的名称,单例运行作用较大。 );
二、事件对象(内核对象)
HANDLE
WINAPI
CreateEventW(
_In_opt_ LPSECURITY_ATTRIBUTES lpEventAttributes, //安全参数
_In_ BOOL bManualReset, //创建手动事件还是自动事件(false),自动事件获取可用信号后自动将信号状态设置为无效。
_In_ BOOL bInitialState, //信号的初始状态是否可用
_In_opt_ LPCWSTR lpName //句柄名称
);
三、关键代码段(非内核对象,存在线程之中)
以下以黄牛抢小米为例说明线程同步三种方式的用法:
首先创建三个变量,分别代表各种方式。
HANDLE hMutext;
HANDLE hEvent;
CRITICAL_SECTION cSecion;
创建一个全局变量,用于存放小米的数量:
int g_MiPhoneCount=100;
创建一个黄牛类,主要包含线程创建,以及抢购方法的实现。
class YellowCow
{
public:
YellowCow(const char *pName):strName(pName){};
static DWORD WINAPI ThreadFunc(void *lpParam )
{
YellowCow *cow=(YellowCow *)lpParam; //利用this指针调用对象函数
cow->Rob();
return 0;
}
void Action()
{
HANDLE hThread = CreateThread(NULL, 0 ,ThreadFunc, this, 0, NULL); //创建一个线程,通过静态函数ThreadFun间接调用类成员函数
CloseHandle(hThread); //关闭线程句柄以便线程结束后及时释放资源
}
virtual void Rob()
{
BOOL bFlag = TRUE;
while (bFlag)
{
WaitForSingleObject(hMutext, INFINITE);//替换区1,信号变量和关键代码段请用以下屏蔽代码分别替换替换区1和2
/*WaitForSingleObject(hEvent, INFINITE);
SetEvent(hEvent);*/
/*EnterCriticalSection(&cSecion);
LeaveCriticalSection(&cSecion);*/
if (g_MiPhoneCount >= 1)
{
printf("%s 抢到编号为 %d的小米手机!!!\n", strName.c_str(), g_MiPhoneCount--);
}
else
{
bFlag = false;
}
ReleaseMutex(hMutext); //替换区2
}
}
private:
string strName;
};
主函数实现:
int _tmain(int argc, _TCHAR* argv[])
{
hMutext = CreateMutex(NULL, FALSE, NULL);
hEvent = CreateEvent(NULL, FALSE, TRUE, NULL);
InitializeCriticalSection(&cSecion);
YellowCow cow1("黄牛1");
YellowCow cow2("黄牛2");
cow1.Action();
cow2.Action();
Sleep(4000);
getchar();
return 0;
}
实现结果: