1. 程式人生 > >vs2010 mfc c++ 多執行緒

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變數,是指示結構是否自動釋放物件的,我按書上說的試驗了一下,竟然不行,繼續鬱悶,不過我還是找到了一個不錯的通用解決辦法,一下是正確解決的完整程式碼:

  1. #include<iostream>
  2. #include "afxwin.h"
  3. usingnamespace std;  
  4. CRITICAL_SECTION cs;  
  5. int s;  
  6. UINT t(LPVOID l)  
  7. {   //::AfxGetThread()->m_bAutoDelete=false;
  8.     ::EnterCriticalSection(&cs);  
  9.     cout<<AfxGetThread()->m_nThreadID<<" "<<s++<<endl;  
  10.     ::LeaveCriticalSection(&cs);  
  11. return 0;  
  12. }  
  13. main()  
  14. {  
  15.     ::InitializeCriticalSection(&cs);  
  16.     CWinThread*  tr[10];  
  17.     HANDLE hh[10];  
  18.     for(int i=0;i<9;i++){  
  19.         tr[i]=::AfxBeginThread(t,0);  
  20.         hh[i]=tr[i]->m_hThread;  
  21.         //::WaitForSingleObject(hh[i],-1);
  22.     }  
  23.     ::WaitForSingleObject(hh[8],-1);  
  24. ::DeleteCriticalSection(&cs);  
  25. //::getchar();
  26. }  

注意編譯的時候要在工程----設定------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

VS2010C++執行同步與互斥簡單運用

繼以往的想法,寫這點文字,貼上點程式碼,是為了增加自己的記憶,也希望能幫助到需要幫助的人。 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 <