1、线程退出
(1)使用布尔型变量,如:
volatile BOOL g_bThreadRun = TRUE;
while(g_bThreadRun)
{
//处理逻辑
}
隐藏问题: 如果线程执行的时间较长,如循环中Sleep(1000); 这样会导致,执行 g_bThreadRun=FALSE; 后立即执行后面的函数,而不会等待线程结束。
(2)设置布尔型变量为FALSE之后,等待线程结束,再执行其他语句,如:
g_bThreadRun
= FALSE;
Sleep(2000);
隐藏问题:这种方式下,只有将Sleep时间给的很长才行,但这样效率很低,并且用户无法让程序立即结束,至少得等Sleep的时间过后才能结束程序,存在着安全隐患。
(3)使用WaitForSingleObject/WaitForMultipleObjects函数,如:
volatile
BOOL g_bThreadRun = TRUE;
//人工事件:一旦将SetEvent之后,等待该事件的所有线程将变成可调度状态
g_hExitEvent
= CreateEvent(NULL, TRUE, FALSE, NULL);
while(g_bThreadRun)
{
if( WaitForSingleObject(g_hExitEvent,1) == WAIT_OBJECT_0)
{
//线程退出
break;
}
//其他处理逻辑
}
2、线程间传送数据
(1)使用由临界区等方式保护的全局变量,如:
int g_nData = 0;
CRITICAL_SECTION g_cs;
InitializeCriticalSection(&g_cs);
//创建临界区
{
EnterCriticalSection(&g_cs);
...
// 操作g_nData
...
LeaveCriticalSection(&g_cs);
return 0;
}
(2)使用PostThreadMessage函数传递线程消息,如:
PostThreadMessage(g_dwThreadID,WM_MY_MSG,0,(LPARAM)(new string("你好"));
【注】接收消息的线程必须有自己的消息队列,如:
MSG msg = {0};
PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);//强制系统创建消息队列
while(g_bThreadRun)
{
if( WaitForSingleObject(g_hExitEvent,1) == WAIT_OBJECT_0)
{
//线程退出
break;
}
if(PeekMessage(&msg,NULL,0,0,PM_REMOVE))
{
if(msg.message != WM_MY_MSG) //用户自定义消息
{
continue;
}
//获取消息
std::string strMsg = *(std::string*)msg.lParam;
if(msg.lParam != NULL)
{
delete (std::string*)msg.lParam;
msg.lParam = NULL;
}
//处理消息strMsg
}
3、类中实现多线程
由于线程函数必须是全局性质的,因此要实现类的多线程,则线程函数必须是static类型或类的friend友元函数。
(1)static线程函数
由于static类型成员函数只能访问static成员,因此可通过以下两种方式访问类的非static成员:
1>定义本类类型的static型成员对象指针m_pThis,将其初始化为类的隐含指针this,在线程函数中通过m_pThis访问非static成员。
2>CreateThread时,将类的隐含指针this通过LPVOID参数的形式传递给线程函数,线程函数通过该参数访问非static成员。
(2)friend线程函数
由于friend友元函数是可以访问类所有成员的全局函数,因此只能以参数的形式将this指针传递给线程函数,以便对类成员的访问。
如:
class MyClass
{
public:
friend DWORD WINAPI MyThread(LPVOID lpParam);
HANDLE m_hThread;
DWORD m_dwThreadID;
};
m_hThread = CreateThread(NULL, 0, MyThread, this, 0, &m_dwThreadID);
DWORD WINAPI MyThread(LPVOID lpParam)
{
if(lpParam == NULL)
{
return -1;
}
MyClass *pThis = (MyClass
*) lpParam;
//线程处理逻辑
}
4、主线程需要所有子线程结束后才可退出,如果是控制台程序,一般由用户从键盘输入来退出程序。
但子线程中也可能会要求用户输入,此时,需要对控制台输入进行互斥控制。如:
HANDLE m_hMutexIo; //控制台输入输出互斥量
BOOL CHttpClient::Wait(const char cStop)
{
if(m_hMutexIo == NULL)
{
m_hMutexIo = CreateMutex(NULL,FALSE,NULL);
if(m_hMutexIo == NULL)
{
return FALSE;
}
}
int c = 0;
while( c != cStop)
{
if( WaitForSingleObject(m_hMutexIo,100) == WAIT_OBJECT_0)
{
c = _getche();
ReleaseMutex(m_hMutexIo);
}
}
if(m_hMutexIo != NULL)
{
CloseHandle(m_hMutexIo);
m_hMutexIo = NULL;
}
return TRUE;
}