vs2010 mfc c++ 多執行緒
(比較推薦使用該方式在MFC下開發)
有關建立執行緒的問題有三種方法:
1.C語言函式,呼叫_beginthread();
2.API函式,呼叫CreateThread();
3.MFC函式,呼叫AfxBeginThread();
推薦使用MFC函式AfxBeginThread();
利用MFC裡的AfxBeginThread函式能很方便地建立執行緒以及對執行緒進行等待、喚醒等操作。
1、函式原型
CWinThread* AfxBeginThread( AFX_THREADPROC pfnThreadProc , LPVOID pParam , int nPriority = THREAD_PRIORITY_NORMAL , UINT nStackSize = 0 , DWORD dwCreateFlags = 0, LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL);
2、引數說明
(1)返回值:一個指向新執行緒的執行緒物件。
(2)pfnThreadProc:執行緒的入口函式,宣告一定要如下:UINT MyThreadFunction( LPVOID pParam );
(3)pParam:傳遞入執行緒的引數,注意它的型別為:LPVOID,所以我們可以傳遞一個結構體入執行緒。
(4)nPriority:執行緒的優先順序,一般設定為 0。讓它和主執行緒具有共同的優先順序。
(5)nStackSize:指定新建立的執行緒的棧的大小。如果為 0,新建立的執行緒具有和主執行緒一樣的大小的棧。
(6)dwCreateFlags:指定建立執行緒以後,執行緒有怎麼樣的標誌。可以指定兩個值:
<1>CREATE_SUSPENDED:執行緒建立以後,會處於掛起狀態,直到呼叫ResumeThread;
<2>0:建立執行緒後就開始執行。
(7)lpSecurityAttrs:指向一個 SECURITY_ATTRIBUTES 的結構體,用它來標誌新建立執行緒的安全性。如果為 NULL,那麼新建立的執行緒就具有和主執行緒一樣的安全性。
3、執行緒建立
一般建立過程如下:
先定義一個工作函式,一般來說你的執行緒就是依照該函式的功能執行任務:
UINT MyThreadFunction( LPVOID pParam )
{
//函式體
return 0;
}
然後可以按以下方式建立執行緒:
CWinThread* MyThread=AfxBeginThread(MyThreadFunction , pParam , THREAD_PRIORITY_NORMAL , 0 , 0 , NULL);
4、執行緒的等待與喚醒
(1)讓執行緒等待(暫時掛起):
MyThread->SuspendThread();
(2)喚醒暫停的執行緒:
MyThread->ResumeThread();
5、檢視執行緒狀態:
DWORD code;
GetExitCodeThread(MyThread->m_hThread , &code);
if(code==STILL_ACTIVE){//執行緒仍在執行}
else {//執行緒停止執行}
6、結束執行緒
TerminateThread(MyThread->m_hThread , 0);
有關建立執行緒的問題有三種方法:
1.C語言函式,呼叫_beginthread();
2.API函式,呼叫CreateThread();
3.MFC函式,呼叫AfxBeginThread();
推薦使用MFC函式AfxBeginThread();
在進行多執行緒程式設計的時候,我們經常用到AfxBeginThread函式來啟動一條執行緒
該函式使用起來非常的簡單方便,其定義如下
CWinThread* AfxBeginThread(
AFX_THREADPROC pfnThreadProc,//執行緒函式地址
LPVOID pParam,//執行緒引數
int nPriority = THREAD_PRIORITY_NORMAL,//執行緒優先順序
UINT nStackSize = 0,//執行緒堆疊大小,預設為1M
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
);
引數說明:
pfnThreadProc:執行緒函式的地址,該引數不能設定為NULL,執行緒函式必須定義成全域性函式或者類的靜態成員函式
例如:
UINT myThreadFunc(LPVOID lparam)
或者
class A
{
public:
static UINT __stdcall myThreadFunc(LPVOID lparam);
}
之所以要定義成類的靜態成員函式,是因為類的靜態成員函式不屬於某個類物件,這樣在呼叫函式
的時候就不用傳遞一個額外的this指標.
pThreadClass:指向從CWinThread派生的子類物件的RUNTIME_CLASS
pParam:要傳遞給執行緒函式的引數
nPriority:要啟動的執行緒的優先順序,預設優先順序為THREAD_PRIORITY_NORMAL(普通優先順序),關於執行緒
優先順序的詳細說明請參考Platform SDK SetThreadPriority函式說明
nStackSize:新執行緒的堆疊大小,如果設定為0,則使用預設大小,在應用程式中一般情況下執行緒的預設堆疊大小
為1M
dwCreateFlags:執行緒建立標誌,該引數可以指定為下列標誌
CREATE_SUSPENDED:以掛起方式啟動執行緒,如果你線上程啟動之前想初始化一些CWinThread類中的一些成員變數
比如:m_bAutoDelete或者你的派生類中的成員變數,當初始化完成之後,你可以使用CWinThread類的ResumeThread
成員函式來恢復執行緒的執行
如果把該標誌設定為0,則表示立即啟動執行緒
lpSecurityAttrs:指向安全描述符的指標,如果使用預設的安全級別只要講該引數設定為NULL就可以了!
上面就是AfxBeginThread函式的簡單說明,我們在使用的時候一般情況下只要指定前兩個引數,其他
引數使用預設值就可以.嗯,的確,使用起來是很簡單,只要這個函式一被呼叫,就建立了一個執行緒.
但是大家有沒有想過,AfxBeginThread函式究竟是如何啟動的執行緒呢?它的內部是如何實現的呢?
最近,由於論文的需求,要用到Windows下的多執行緒。考慮到介面用MFC寫了,於是上網搜了下MFC下的多執行緒怎樣搞,都說用AfxBeginThread來日比較好。哥向來比較浮躁,先搜搜有沒相關程式碼,找到幾個可用的,然後各種摘抄,於是乎將哥的播放器的幾個執行緒搞成下面這段程式碼(摘要):
UINT playThread(LPVOID pParam){ //播放執行緒,固定格式
//......做變數宣告,賦值等前期工作
while(SomeCondition){ //播放執行緒的迴圈
//......播放音樂,不解析
}
return 0;
}
void CPlayerDlg::OnBnClickedPlay(){ //播放按鈕響應函式
if(isThreadPause){ //判斷是否暫停中
isThreadPause=false;
pPlayerThread->ResumeThread();//繼續播放
}
else{
OnBnClickedStop();
pPlayerThread=AfxBeginThread(playWaveThread,NULL); //開啟播放執行緒
}
}
void CPlayerDlg::OnBnClickedPause(){ //暫停響應函式
if(!isThreadPause){
PlayerThread->SuspendThread(); //掛起程序,相當於暫停播放
isThreadPause=true;
}
}
void CPlayerDlg::OnBnClickedStop(){ //終止響應函式
if(pPlayThread){
isThreadPause=false;
TerminateThread(pPlayerThread->m_hThread,0);//強行終止執行緒,這裡有問題,後面說
}
}
其中播放執行緒playThread的宣告是固定那種格式的,而且最好寫成全域性函式,方便,如果寫成類成員函式的話又要加static,呼叫時又要加作用域的,十分蛋痛。寫完後果斷執行,yeah,能播放、暫停和停止,相當舒服,也沒去理會細節的問題。
直到今天,心血來潮地開啟工作管理員,看看程式記憶體佔用情況,發現了一個狠嚴重的問題:每當我停止一首歌,播放下一首時,記憶體就突然間往上跳。一開始以為是正常的記憶體建立和回收造成的浮動,但我繼續不斷地重複播放停止、播放停止,發現記憶體一直往上升。雖然每次都只是上升一點點,但明擺著的memory leak擱在那,還不搞它哥以後怎樣出來混?
好,果斷google之,發現問題出在TerminateThread這個函式。這個TerminateThread結束執行緒用的是相當暴力的方法,據說連裡面的區域性變數都不釋放。這就草了,馬上尋找解決辦法,有人回帖說用CreateEvent和WaitForSingleObject結合日之,解釋沒解釋清楚,給出的sample code也是相當糾結和羞澀,而且樓下跟帖說這種方法有可能阻塞死鎖之類的。果斷放棄,看到另外一種方法,就是在停止的響應函式裡用::PostThreadMessage(由於播放執行緒是全域性函式,所以前面要加::)給播放執行緒傳送停止訊息,播放執行緒里加一個MSG的變數和while,每次裡面呼叫PeekMessage來檢查是否發來停止的訊息,寫了下,程式碼相當簡練明瞭:
#define WM_THREAD_STOP 0x0427 //自定義一個訊息,也可以用系統定義的如WM_QUIT
UINT playWaveThread(LPVOID pParam){
//......做變數宣告,賦值等前期工作
while(SomeCondition){ //播放執行緒的迴圈
MSG msg; //增加一個MSG的變數msg來接收訊息
while(PeekMessage(&msg,NULL,0,0,PM_REMOVE)){ //將訊息佇列裡的訊息逐個讀入msg
if(ssage==WM_THREAD_STOP){ //如果收到終止訊息則退出
//TODO:放在堆裡的變數要在這裡手動清理
return 0; //執行緒正常返回,會釋放區域性變數等記憶體資源
}
else{
DispatchMessage(&msg);//字面意思,不解釋
}
}
//......播放音樂,不解析
}
return 0;//正常播放結束,釋放資源
}
void CPlayerDlg::OnBnClickedPlay(){……}//播放按鈕響應函式,不變
void CPlayerDlg::OnBnClickedPause(){……}//暫停響應函式,也不變
void CPlayerDlg::OnBnClickedStop(){
if(pPlayerThread){
isThreadPause=false;
//原來的TerminateThread不用,換成下面這個
::PostThreadMessage(pPlayerThread->m_nThreadID,WM_THREAD_STOP,0,0);
}
}
寫完,果斷執行並開啟工作管理員監測,誒!果然沒有出現之前的記憶體一直在漲的現象,十分舒服,搞定收工!話說本人剛學多執行緒,程式碼寫得相當的水,如果哪位大牛看到這處理方法還存在什麼問題望不吝賜教,謝謝!
建立一個Win32 Console Application,工程名為CreateMythread
新增一個CPP檔案,程式碼如下
#include <iostream>
#include <windows.h>
using namespace std;
DWORD WINAPI ThreadProc(LPVOID pParam);
UINT PrintHello(LPVOID lpParam);
HANDLE g_Mutex; //互斥量
int main(int argc,char* argv[])
{
//建立互斥量
g_Mutex=CreateMutex(NULL,false,"CreateMythread");
DWORD ThreadID;
char* cParam="Hello World!";
int iParam=2010;
//建立第一個執行緒ThreadProc
CreateThread(NULL,0,ThreadProc,
cParam,0,&ThreadID);
//建立第二個執行緒PrintHello
//若不是規範格式則必須用LPTHREAD_START_ROUTINE轉換
CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)PrintHello,
&iParam,0,&ThreadID);
Sleep(100);
CloseHandle(g_Mutex);
//system("pause");
return 0;
}
DWORD WINAPI ThreadProc(LPVOID pParam)
{
//等待被喚醒
WaitForSingleObject(g_Mutex,INFINITE);
cout <<"CreateThread:ThreadProc ";
cout <<(char*)pParam <<endl;
//Sleep(0);
//將互斥量設定為有訊號,此時第一個等待的執行緒被喚醒,並將該互斥量置為無訊號狀態
ReleaseMutex(g_Mutex);
return 0;
}
UINT PrintHello(LPVOID lpParam)
{
WaitForSingleObject(g_Mutex,INFINITE);
cout <<"CreateThread:PrintHello ";
cout <<*((int*)lpParam) <<endl;
//Sleep(0);
ReleaseMutex(g_Mutex);
return 0;
}
最近看一本書<windows程式設計>,書寫的不錯,很通俗易懂,我對其中的一些例子自己也做了練習,學到了不少.在我看執行緒這一塊時,還是有不少感悟.
在看到afxbeginthread時,對這個方法蠻感興趣的,建立執行緒很方便,但是我注意了下,該函式返回的不是建立執行緒的控制代碼,而是cwndthread的指標物件,而我想用WaitForMultipleObjects等待執行緒返回,該怎麼辦呢,cwndthread裡有一個m_hThread物件,是建立執行緒的控制代碼.我以為這樣就可以了,但是在實際寫程式碼中卻出現了很大的問題,WaitForMultipleObjects不起作用,我很鬱悶,看了下cwndthread的結構以及實現程式碼,才恍然大悟,原來,用afxbeginthread建立的執行緒在結束時,會自動釋放物件,關閉控制代碼,清理記憶體,這一切都是在cwndthread的解構函式裡進行,不需要外界的干預,看了下說明,還有個m_bAutoDelete變數,是指示結構是否自動釋放物件的,我按書上說的試驗了一下,竟然不行,繼續鬱悶,不過我還是找到了一個不錯的通用解決辦法,一下是正確解決的完整程式碼:
- #include<iostream>
- #include "afxwin.h"
- usingnamespace std;
- CRITICAL_SECTION cs;
- int s;
- UINT t(LPVOID l)
- { //::AfxGetThread()->m_bAutoDelete=false;
- ::EnterCriticalSection(&cs);
- cout<<AfxGetThread()->m_nThreadID<<" "<<s++<<endl;
- ::LeaveCriticalSection(&cs);
- return 0;
- }
- main()
- {
- ::InitializeCriticalSection(&cs);
- CWinThread* tr[10];
- HANDLE hh[10];
- for(int i=0;i<9;i++){
- tr[i]=::AfxBeginThread(t,0);
- hh[i]=tr[i]->m_hThread;
- //::WaitForSingleObject(hh[i],-1);
- }
- ::WaitForSingleObject(hh[8],-1);
- ::DeleteCriticalSection(&cs);
- //::getchar();
- }
注意編譯的時候要在工程----設定------general裡設定use mfc in a shared dll即可,否則會報錯.
我說一下解決思路,因為afxbeginthread建立的執行緒執行後會自動釋放物件,也就是說控制代碼無效了,WaitForMultipleObjects自然也不起作用了,於是我用WaitForSingleObject等待最後一個執行緒建立的控制代碼便可以了.
以後只是我個人理解,如有不妥,或有更好的辦法,還請多多指教.
相關推薦
vs2010 mfc c++ 多執行緒
(比較推薦使用該方式在MFC下開發) 有關建立執行緒的問題有三種方法: 1.C語言函式,呼叫_beginthread(); 2.API函式,呼叫CreateThread(); 3.MFC函式,呼叫AfxBeginThread(); 推薦使用MFC函式AfxBeginTh
【VS2010】C++多執行緒同步與互斥簡單運用
繼以往的想法,寫這點文字,貼上點程式碼,是為了增加自己的記憶,也希望能幫助到需要幫助的人。 1. 互斥量,Mutex #include <Windows.h> #include <iostream> usingnamespace
C++多執行緒同步技術(MFC)
1. 何時使用同步類 MFC 提供的多執行緒類分為兩類:同步物件(CSyncObject、CSemaphore、CMutex、CCriticalSection 和 CEvent)和同步訪問物件(CMultiLock 和 CSingleLock)。 當必須控制對資源的
使用 C++ 和 MFC 進行多執行緒程式設計
程序是應用程式的執行例項。例如,雙擊“記事本”圖示時,將啟動執行“記事本”的程序。 執行緒是程序內的執行路徑。啟動“記事本”時,作業系統建立程序並開始執行該程序的主執行緒。此執行緒終止時,程序也終止。啟動程式碼以函式地址的形式將此主執行緒提供給作業系統。通常是所提供的main 函式或 WinMain 函式
c/c++ 多執行緒 std::lock
多執行緒 std::lock 當要同時操作2個物件時,就需要同時鎖定這2個物件,而不是先鎖定一個,然後再鎖定另一個。同時鎖定多個物件的方法:std::lock(物件1.鎖,物件2.鎖...) 額外說明:lock_guard<mutex> lock_a(d1.m, std::adopt_lock
C#多執行緒順序依賴執行控制
在開發過程中,經常需要多個任務並行的執行的場景,同時任務之間又需要先後依賴的關係。針對這樣的處理邏輯,通常會採用多執行緒的程式模型來實現。 比如A、B、C三個執行緒,A和B需要同時啟動,並行處理,且B需要依賴A完成,在進行後續的處理,C需要B完成後開始處理。
C/C++ 多執行緒機制
一、C/C++多執行緒操作說明 C/C++多執行緒基本操作如下: 1. 執行緒的建立結束 2. 執行緒的互斥和同步 3. 使用訊號量控制執行緒 4. 執行緒的基本屬性配置 在C/C++程式碼編寫時,使用多執行緒機制,首先需要做的事情就是宣告引用,具體如下
c/c++ 多執行緒 std::once
多執行緒 std::once 轉自:https://blog.csdn.net/hengyunabc/article/details/33031465 std::once的特點:即使有多個執行緒要訪問同一個函式,只有一個執行緒會成功。 std::once的用途:當某個資料只有在初始化的時候需要執行緒安全
C#多執行緒基礎(多執行緒的優先順序、狀態、同步)
一、關於多執行緒的優先順序、狀態、同步指令碼如下: using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System
C++多執行緒之std::thread
C++11,包含標頭檔案 thread.h,並使用名稱空間std。 thread類提供的方法 方法 描述 thread 建構函式,在這裡傳入執行緒執行函式,和函式引數
C++ 多執行緒pthread 學習筆記
本篇是我在學習C++多執行緒的時候做的筆記,主要記錄的是基礎的流程,部分程式碼例項,以及重點函式的說明。 pthread 入口函式型別說明 void * func1(void * t) void* 表示無型別指標 void*作為函式引數,表示函式接收一個指標,不管是什麼型別
2017.10.21 C# 多執行緒控制控制元件例項
直接上程式碼片段 using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Text
2017.10.12 C#多執行緒與非同步的區別
最近在寫個多執行緒處理的程式,又重新溫習了一下相關知識,記錄在這裡。 C#多執行緒與非同步的區別 原文地址:http://kb.cnblogs.com/page/116095/ 多執行緒和非同步操作的異同 多執行緒和非同步操作兩者都可以達到避免呼叫執行緒阻塞的目的,從而提高軟體
linux c 多執行緒開發
在開發多執行緒程式時,當建立的執行緒數量特別多的時候,就會遇到執行緒數量的瓶頸。 多執行緒設定 設定核心引數 kernel.threads-max kernel.threads-max 是 linux 系統允許建立的最大執行緒數,預設是 7767 修改 /etc/sysc
C++:蟻群演算法解決TSP(C++多執行緒版)
TSP問題:旅行商問題,最短迴路。 這裡採用att48資料,鄰接矩陣全部取整數,原資料放在文後。 解決程式碼如下: //#define TEST_INPUT //#define TEST_T //#define TEST_ANT //#define TEST_VALUE #
c/c++ 多執行緒 ubuntu18.04 boost編譯與執行的坑
多執行緒 boost編譯與執行的坑 背景:因為要使用boost裡的多執行緒庫,所以遇到了下面的坑。 系統版本:ubuntu18.04 一,安裝boost 1,去boost官網下載 boost_1_XX_0.tar.gz 2,解壓 tar -zxvf boost_1_65_0.tar.gz 3
c/c++ 多執行緒 boost的讀寫(reader-writer)鎖
多執行緒 boost的讀寫(reader-writer)鎖 背景:保護很少更新的資料結構時,c++標準庫沒有提供相應的功能。 例如:有個DNS條目快取的map,基本上很少有更新,大部分都是讀取,但是偶爾也會有更新,這種情況下,如果在讀取的函式里加上std::mutex就過於悲觀了,每次只能有一個執行緒讀取
C# 多執行緒學習系列二
一、關於前臺執行緒和後臺執行緒 1、簡介 CLR中執行緒分為兩種型別,一種是前臺執行緒、另一種是後臺執行緒. 前臺執行緒:應用程式的主執行緒、Thread構造的執行緒都預設為前臺執行緒 後臺執行緒:執行緒池執行緒都為後臺執行緒 2、區別 前臺執行緒:前臺執行緒一般執行重要性很高的任
C# 多執行緒學習系列四之取消、超時子執行緒操作
1、簡介 雖然ThreadPool、Thread能開啟子執行緒將一些任務交給子執行緒去承擔,但是很多時候,因為某種原因,比如子執行緒發生異常、或者子執行緒的業務邏輯不符合我們的預期,那麼這個時候我們必須關閉它,而不是讓它繼續執行,消耗資源.讓CPU不在把時間和資源花在沒有意義的程式碼上.
C++多執行緒中的future(期望)
Providers std::promise 和std::future配合使用,給std::future傳遞期望值,下面是最簡單的一個用法: #include <iostream> #include <functional> #include <