這裏廢話不多講了,因爲項目原因,需要開啓線程進行處理,在不瞭解線程的情況下,直接百度一下,然後就使用了,結果可想而知,出現了異常,所以花了一天時間系統學習一下多線程,這裏主要是針對win32編程方面的線程介紹,更多偏向於MFC的多線程開發。
1.線程的創建
創建線程的三種方式:
方式一: CreatThread(記得關閉線程句柄)
方式二:AfxBeginThread(會自動釋放,可以設置不自動釋放)
方式三:_beginthreadex(記得關閉線程句柄)
其中方式二和方式三呢都會間接的調用方式一,因爲方式一是win32的線程調用API,而MFC爲了用戶更容易使用多線程進行了封裝,其實他們都差多的,下面針對三種方式進行梳理:
大家記得下載微軟的MSDN文檔查詢相關函數的說明文檔,至於如何添加文檔,請參考這篇文章設定,下面很多內容都是官方文檔的內容。
例子:方式一: CreatThread(記得關閉線程句柄)
static HANDLE CreateThread(
LPSECURITY_ATTRIBUTES lpsa,
DWORD dwStackSize,
LPTHREAD_START_ROUTINE pfnThreadProc,
void* pvParam,
DWORD dwCreationFlags,
DWORD* pdwThreadId
) throw( );
/*
lpsa:新線程的安全特性。
dwStackSize:新線程的堆棧大小。
pfnThreadProc:線程函數。
pvParam:將傳遞的參數傳遞給線程過程。
dwCreationFlags:創建標誌(0個或CREATE_SUSPENDED)。
pdwThreadId: [out] 中,若成功,接收新創建的線程的線程ID DWORD變量的地址。
*/
使用例程:
//線程函數
DWORD WINAPI ThreadProc(LPVOID lpParameter)
{
int tipMes = (int)lpParameter;
CString strMsg;
strMsg.Format(_T("%d"), tipMes);
AfxMessageBox(strMsg);
return 0;
}
void CThreadTestDlg::OnBnClickedCreatthreadButton()
{
//需要給其傳參
DWORD dwthreadID = 0;
HANDLE hThread = CreateThread(NULL, 0, ThreadProc, (LPVOID)123, 0, &dwthreadID);
//需要調用CloseHandle,關閉線程句柄,避免泄漏
CloseHandle(hThread);
}
方式二:AfxBeginThread(會自動釋放)
該方式是MFC中的創建方式,主要有兩種:界面線程和工作線程
工作線程:
CWinThread* AfxBeginThread(
AFX_THREADPROC pfnThreadProc,
LPVOID pParam,
int nPriority = THREAD_PRIORITY_NORMAL,
UINT nStackSize = 0,
DWORD dwCreateFlags = 0,
LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL
);
界面線程:
CWinThread* AfxBeginThread(
CRuntimeClass* pThreadClass,
int nPriority = THREAD_PRIORITY_NORMAL,
UINT nStackSize = 0,
DWORD dwCreateFlags = 0,
LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL
);
界面線程和工作線程的最大的區別在於使用具有消息循環
其中上面的參數和方式一一樣的,只是這裏有默認的參數了
//方式二的線程創建的例子
UINT __cdecl MyControllingFunction(LPVOID pParam)
{
int tipMes = (int)pParam;
CString strMsg;
while (TRUE)
{
strMsg.Format(_T("%d"), tipMes++);
OutputDebugString(strMsg);
Sleep(100);
}
return 0;
}
//方式二實現線程的創建
CWinThread* pTherad = AfxBeginThread(MyControllingFunction, (LPVOID)100);
第三種大家自己看說明文檔吧。
2.線程函數如何調用類成員呢?如何調用主對話框的成員?
方法一:是通過傳入參數,即傳入這個主對話框的指針即可,如下操作
//方式二實現線程的創建
//m_Num變量是主對話框的成員變量
public:
m_Num = 123;
//傳入當前主對話框的指針,不直接把變量的地址傳過去
CWinThread* pTherad = AfxBeginThread(MyControllingFunction, this);
************************線程函數*************************************************
UINT __cdecl MyControllingFunction(LPVOID pParam)
{
//通過強制類型轉換得到主對話框的指針變量,進而可以通過訪問其成員變量了
CThreadTestDlg* pThisDlg = (CThreadTestDlg*)pParam;
int tipMes = pThisDlg->m_Num;
CString strMsg;
//信息打印
while (TRUE)
{
strMsg.Format(_T("%d"), tipMes++);
OutputDebugString(strMsg);
Sleep(100);
}
return 0;
}
方法二:
把線程函數作爲主對話框類的成員函數:
//方法就是把線程函數稱爲對話框類的靜態成員函數如下:
static UINT __cdecl MyControllingFunction(LPVOID pParam);
//然後帶上作用域調用即可
//方式二的線程執行函數的例子
UINT __cdecl CThreadTestDlg::MyControllingFunction(LPVOID pParam)
3.MFC中的工作線程和界面線程的區別
界面線程和工作線程的最大的區別在於使用具有消息循環,我們先看看在工作線程中創建窗口的情況:
////不建議在工作線程中進行窗口的操作
//TestDlg dlg;
////模態對話框
//dlg.DoModal();
上面創建的模態對話框是可以使用的,但是不建議使用
//在工作線程中創建一個非模態對話框,看看效果
TestDlg* pTestDlg = new TestDlg();
pTestDlg->Create(IDD_DIALOG1, NULL);
pTestDlg->ShowWindow(SW_SHOW);
//會發現,窗口一閃而過,是因爲在創建的時,線程也就執行完了,
//資源會被釋放,因此窗口會消失,同時在窗口上無法操作,主要原因是無法接受到消息信息,在後面添加消息循環就可以了
MSG msg = { 0 };
while (GetMessage(&msg,NULL,0,0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
最後既然都在工作線程中添加而了消息循環,爲什麼不直接使用界面線程呢?
同時界面線程的消息循環要比我們自己在工作線程中創建的消息循環更完備
界面線程:
界面線程的創建:
1.從CWinThread類派生自己的類:CUIThreadApp;
2.重載InitInstance(必須重載)與ExitInstance(可選重載)函數
3.在InitInstance函數彙總進行界面的創建
4.調用AfxBeginThread函數開啓界面線程:
AfxBeginThread(RUNTIME_CLASS(CUIThreadApp));
這裏大家參考別人的吧,因爲我戰時不涉及界面線程,因此沒仔細學習這個線程。