您的位置:首页 > 编程语言 > C语言/C++

vs2010 mfc c++ 多线程

2016-05-22 15:38 435 查看
(比较推荐使用该方式在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_ATTRIBUTESlpSecurityAttrs = 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_CLASSpParam:要传递给线程函数的参数nPriority:要启动的线程的优先级,默认优先级为THREAD_PRIORITY_NORMAL(普通优先级),关于线程 优先级的详细说明请参考Platform SDK SetThreadPriority函数说明nStackSize:新线程的堆栈大小,如果设置为0,则使用默认大小,在应用程序中一般情况下线程的默认堆栈大小 为1MdwCreateFlags:线程创建标志,该参数可以指定为下列标志 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变量,是指示结构是否自动释放对象的,我按书上说的试验了一下,竟然不行,继续郁闷,不过我还是找到了一个不错的通用解决办法,一下是正确解决的完整代码:[cpp] view plain copy#include<iostream>  #include "afxwin.h"  using namespace 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等待最后一个线程创建的句柄便可以了.        以后只是我个人理解,如有不妥,或有更好的办法,还请多多指教.
                                            
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  c++