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 
#include 
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 < 
   
    //Sleep(0);
    //将互斥量设置为有信号,此时第一个等待的线程被唤醒,并将该互斥量置为无信号状态
    ReleaseMutex(g_Mutex);
    return 0;
}
UINT PrintHello(LPVOID lpParam)
{
    WaitForSingleObject(g_Mutex,INFINITE);
    cout <<"CreateThread:PrintHello ";
    cout <<*((int*)lpParam) < 
   
    //Sleep(0);
    ReleaseMutex(g_Mutex);
    return 0;
}
 
   
 
   
 
   
 
   
 
   

最近看一本书,书写的不错,很通俗易懂,我对其中的一些例子自己也做了练习,学到了不少.在我看线程这一块时,还是有不少感悟. 

      在看到afxbeginthread时,对这个方法蛮感兴趣的,创建线程很方便,但是我注意了下,该函数返回的不是创建线程的句柄,而是cwndthread的指针对象,而我想用WaitForMultipleObjects等待线程返回,该怎么办呢,cwndthread里有一个m_hThread对象,是创建线程的句柄.我以为这样就可以了,但是在实际写代码中却出现了很大的问题,WaitForMultipleObjects不起作用,我很郁闷,看了下cwndthread的结构以及实现代码,才恍然大悟,原来,用afxbeginthread创建的线程在结束时,会自动释放对象,关闭句柄,清理内存,这一切都是在cwndthread的析构函数里进行,不需要外界的干预,看了下说明,还有个m_bAutoDelete变量,是指示结构是否自动释放对象的,我按书上说的试验了一下,竟然不行,继续郁闷,不过我还是找到了一个不错的通用解决办法,一下是正确解决的完整代码:

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

注意编译的时候要在工程----设置------general里设置use mfc in a shared dll即可,否则会报错.

我说一下解决思路,因为afxbeginthread创建的线程运行后会自动释放对象,也就是说句柄无效了,WaitForMultipleObjects自然也不起作用了,于是我用WaitForSingleObject等待最后一个线程创建的句柄便可以了.

        以后只是我个人理解,如有不妥,或有更好的办法,还请多多指教.


你可能感兴趣的:(c++)