(比较推荐使用该方式在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变量,是指示结构是否自动释放对象的,我按书上说的试验了一下,竟然不行,继续郁闷,不过我还是找到了一个不错的通用解决办法,一下是正确解决的完整代码:
- #include
- #include "afxwin.h"
- using namespace std;
- CRITICAL_SECTION cs;
- int s;
- UINT t(LPVOID l)
- { //::AfxGetThread()->m_bAutoDelete=false;
- ::EnterCriticalSection(&cs);
- cout<
m_nThreadID<<" "< - ::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等待最后一个线程创建的句柄便可以了.
以后只是我个人理解,如有不妥,或有更好的办法,还请多多指教.