MFC全局函数::AfxBeginThread
函数功能描述:创建新的线程函数原型:
CWinThread* AfxBeginThread( AFX_THREADPROC pfnThreadProc, LPVOID pParam, int nPriority =
THREAD_PRIORITY_NORMAL, UINT nStackSize = 0, 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:工作线程的函数指针,不可以为空。并且工作线程的函数必须如此声明:
UINT MyControllingFunction( LPVOID pParam );
pThreadClass: 从CWinThread类继承来的对象的RUNTIME_CLASS指针。
pParam: 传递给工作线程函数pfnThreadProc的参数。
nPriority: 线程的优先级。如果为0,则与创建它的线程优先级相同。可以通过参考Win32 Programmer’s
Reference中的SetThreadPriority得到所有可用的优先级列表和描述。
nStackSize: 以字节为单位指定新线程的堆栈大小。如果为0,则与创建它的线程的堆栈大小相同。
dwCreateFlags:指定一个额外的标志控制线程的产生。它可以包括下面两个值中的一个:
CREATE_SUSPENDED:以挂起模式开始线程,并且指定挂起次数.当调用ResumeThread时,这个
线程才会被执行。
0 :创建之后,马上执行线程。
lpSecurityAttrs:指向SECURITY_ATTRIBUTES结构的指针,结构中指定了线程的安全属性。如果为NULL,则与
创建它的线程的安全属性相同。如果希望得到更多的有关SECURITY_ATTRIBUTES结构的信息,
请参考Win32 Programmer’s Reference。
注释:
调用这个函数创建一个新的线程。第一种形式的AfxBeginThread创建一个工作线程;第二种形式创建一个用户
接口线程。
AfxBeginThread创建一个新的CWinThread对象,调用它的CreateThread函数开始执行线程并且返回指向线程的指
针。Checks are made throughout the procedure to make sure all objects are deallocated properly
should any part of the creation fail. 终止线程,可以在线程函数中调用AfxEndThread, 或者从工作线程
的函数中返回。
了解更多的有关AfxBeginThread的信息,可以参考文章 Multithreading: Creating Worker Threads 和
Multithreading: Creating User-Interface Threads in Visual C++ Programmer’s Guide.
参看:AfxGetThread
示例:
创建一个工作线程:
UINT WorkForce(LPVOID lpParameter);//线程函数声明
CWinThread *pMyFirstWorker,*pMySecondWorker;
LPVOID pParam = NULL;
int nPriority = THREAD_PRIORITY_ABOVE_NORMAL;//默认为THREAD_PRIORITY_NORMAL
UINT nStackSize = 0;//与创建它的线程堆栈大小相同
DWORD dwCreateFlags = 0;//创建后立即执行
LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL ;//与创建它的线程安全属性相同
pMyFirstWorker=AfxBeginThread( (AFX_THREADPROC)WorkForce, pParam, nPriority , nStackSize,
dwCreateFlags , lpSecurityAttrs);
pMySecondWorker=AfxBeginThread( (AFX_THREADPROC)WorkForce, pParam);//如果采用默认值
DWORD WINAPI WorkForce( LPVOID lpParameter // 线程所需参数,可以通过它传递数据)
{
return 0;//什么不做
}
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函数究竟是如何启动的线程呢?它的内部是如何实现的呢?
下面我们就来看一下AfxBeginThread函数的内部实现
//启动worker线程
CWinThread* AFXAPI AfxBeginThread(AFX_THREADPROC pfnThreadProc, LPVOID pParam,
int nPriority, UINT nStackSize, DWORD dwCreateFlags,
LPSECURITY_ATTRIBUTES lpSecurityAttrs)
{
#ifndef _MT
pfnThreadProc;
pParam;
nPriority;
nStackSize;
dwCreateFlags;
lpSecurityAttrs;
return NULL;
#else
ASSERT(pfnThreadProc != NULL);
CWinThread* pThread = DEBUG_NEW CWinThread(pfnThreadProc, pParam);
ASSERT_VALID(pThread);
if (!pThread->CreateThread(dwCreateFlags|CREATE_SUSPENDED, nStackSize,
lpSecurityAttrs))
{
pThread->Delete();
return NULL;
}
VERIFY(pThread->SetThreadPriority(nPriority));
if (!(dwCreateFlags & CREATE_SUSPENDED))
VERIFY(pThread->ResumeThread() != (DWORD)-1);
return pThread;
#endif //!_MT)
}
//启动UI线程
CWinThread* AFXAPI AfxBeginThread(CRuntimeClass* pThreadClass,
int nPriority, UINT nStackSize, DWORD dwCreateFlags,
LPSECURITY_ATTRIBUTES lpSecurityAttrs)
{
#ifndef _MT
pThreadClass;
nPriority;
nStackSize;
dwCreateFlags;
lpSecurityAttrs;
return NULL;
#else
ASSERT(pThreadClass != NULL);
ASSERT(pThreadClass->IsDerivedFrom(RUNTIME_CLASS(CWinThread)));
CWinThread* pThread = (CWinThread*)pThreadClass->CreateObject();
if (pThread == NULL)
AfxThrowMemoryException();
ASSERT_VALID(pThread);
pThread->m_pThreadParams = NULL;
if (!pThread->CreateThread(dwCreateFlags|CREATE_SUSPENDED, nStackSize,
lpSecurityAttrs))
{
pThread->Delete();
return NULL;
}
VERIFY(pThread->SetThreadPriority(nPriority));
if (!(dwCreateFlags & CREATE_SUSPENDED))
VERIFY(pThread->ResumeThread() != (DWORD)-1);
return pThread;
#endif //!_MT
}
从上面的代码中可以看出AfxBeginThread所做的事情主要有以下几点:
1.在heap中配置一个新的CWinThread对象(worker线程)
代码如:CWinThread* pThread = DEBUG_NEW CWinThread(pfnThreadProc, pParam);
调用CRuntimeClass结构中的CreateObject函数创建CWinThread对象
CWinThread* pThread = (CWinThread*)pThreadClass->CreateObject();
CRuntimeClass以及MFC相关类的内部实现,详情请参考
《深入浅出MFC》侯捷著
2.调用CWinThread::CreateThread()并设定属性,使线程以挂起状态产生
pThread->CreateThread(dwCreateFlags|CREATE_SUSPENDED, nStackSize,lpSecurityAttrs);
3.设定线程的优先权
pThread->SetThreadPriority(nPriority);
4.调用CWinThread::ResumeThread
pThread->ResumeThread();
通过上面的说明,我想大家对该函数到底在内部都做了什么,应该有一个初步的了解了!
简介
用户界面线程和工作者线程都是由AfxBeginThread创建的。现在,考察该函数:MFC提供了两个重载版的AfxBeginThread,一个用于用户界面线程,另一个用于工作者线程,分别有如下的原型和过程: 用户界面线程的AfxBeginThread 用户界面线程的AfxBeginThread的原型如下: CWinThread* AFXAPI AfxBeginThread( CRuntimeClass* pThreadClass, int nPriority, UINT nStackSize, DWORD dwCreateFlags, LPSECURITY_ATTRIBUTES lpSecurityAttrs) 其中: 参数1是从CWinThread派生的RUNTIME_CLASS类; 参数2指定线程优先级,如果为0,则与创建该线程的线程相同; 参数3指定线程的堆栈大小,如果为0,则与创建该线程的线程相同; 参数4是一个创建标识,如果是CREATE_SUSPENDED,则在悬挂状态创建线程,在线程创建后线程挂起,否则线程在创建后开始线程的执行。 参数5表示线程的安全属性,NT下有用。 工作者线程的AfxBeginThread 工作者线程的AfxBeginThread的原型如下: CWinThread* AfxBeginThread(AFX_THREADPROC pfnThreadProc, LPVOID lParam, int nPriority = THREAD_PRIORITY_NORMAL, UINT nStackSize = 0, DWORD dwCreateFlags = 0, LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL );//用于创建工作者线程 返回值: 一个指向新线程的线程对象的指针 pfnThreadProc : 线程的入口函数,声明一定要如下: UINT MyThreadFunction(LPVOID pParam),不能设置为NULL; pParam : 传递入线程的参数,注意它的类型为:LPVOID,所以我们可以传递一个结构体入线程. nPriority : 线程的优先级,一般设置为 0 .让它和主线程具有共同的优先级. nStackSize : 指定新创建的线程的栈的大小.如果为 0,新创建的线程具有和主线程一样的大小的栈 dwCreateFlags : 指定创建线程以后,线程有怎么样的标志.可以指定两个值: CREATE_SUSPENDED : 线程创建以后,会处于挂起状态,直到调用:ResumeThread 0 : 创建线程后就开始运行. lpSecurityAttrs : 指向一个 SECURITY_ATTRIBUTES 的结构体,用它来标志新创建线程的安全性.如果为 NULL, 那么新创建的线程就具有和主线程一样的安全性. 如果要在线程内结束线程,可以在线程内调用 AfxEndThread. 结束线程的两种方式 当你在后台用线程来打印一些图形时.有时在打印一部分后,你希望可以停下来,那么此如何让线程停止呢.编辑本段常见用法
AfxBeginThread(ThreadProc,this);编辑本段要结束线程的两种方式
1 : 这是最简单的方式,也就是让线程函数执行完成,此时线程正常结束.它会返回一个值,一般0是成功结束, 当然你可以定义自己的认为合适的值来代表线程成功执行.在线程内调用AfxEndThread将会直接结束线程,此时线 程的一切资源都会被回收. 2 : 如果你想让另一个线程B来结束线程A,那么,你就需要在这两个线程中传递信息. 不管是工作者线程还是界面线程,如果你想在线程结束后得到它的结果,那么你可以调用: ::GetExitCodeThread函数
在进行多线程程序设计的时候,我们经常用到AfxBeginTh read函数来启动一条线程该函数使用起来非常的简单方便,其定 义如下
CWinThread* AfxBeginThr ead( AFX_THREADPROC pfnThrea dProc,//线程函数地址 LPVOID pParam ,//线程参数 int nPriority = THRE AD_PRIORITY_NORMAL,//线程优先级 U INT nStackSize = 0,//线程堆栈大小,默认 为1M DWORD dwCreateFlags = 0, // LPSECURITY_ATTRIBUTES lpS ecurityAttrs = NULL );
CWin Thread* AfxBeginThread( CRun timeClass* pThreadClass, int nPriority = THREAD_PRIORITY_N ORMAL, UINT nStackSize = 0, DWORD dwCreateFlags = 0, L PSECURITY_ATTRIBUTES lpSecurit yAttrs = NULL );
参数说明:pfnTh readProc:线程函数的地址,该参数不能设置为NULL, 线程函数必须定义成全局函数或者类的静态成员函数例如:UINT myThreadFunc(LPVOID lparam)或者 class A{public: static UINT __stdcall myThreadFunc(LP VOID lparam);
}之所以要定义成类的静态成员函数, 是因为类的静态成员函数不属于某个类对象,这样在调用函数的时候 就不用传递一个额外的this指针.
pThreadCl ass:指向从CWinThread派生的子类对象的RUNTI ME_CLASS
pParam:要传递给线程函数的参数
nPriority:要启动的线程的优先级,默认优先级 为THREAD_PRIORITY_NORMAL(普通优先级) ,关于线程 优先级的详细说明请参考Platform SDK SetThreadPriority函数说明
nStac kSize:新线程的堆栈大小,如果设置为0,则使用默认大小, 在应用程序中一般情况下线程的默认堆栈大小 为1M
dw CreateFlags:线程创建标志,该参数可以指定为下列标 志 CREATE_SUSPENDED:以挂起方式启动线程,如 果你在线程启动之前想初始化一些CWinThread类中的一些 成员变量 比如:m_bAutoDelete或者你的派生类中的 成员变量,当初始化完成之后,你可以使用CWinThread类 的ResumeThread 成员函数来恢复线程的运行 如果把 该标志设置为0,则表示立即启动线程lpSecurityAtt rs:指向安全描述符的指针,如果使用默认的安全级别只要讲该参 数设置为NULL就可以了!
上面就是AfxBeginT hread函数的简单说明,我们在使用的时候一般情况下只要指定 前两个参数,其他参数使用默认值就可以.嗯,的确,使用起来是很 简单,只要这个函数一被调用,就创建了一个线程.但是大家有没有 想过,AfxBeginThread函数究竟是如何启动的线程呢 ?它的内部是如何实现的呢?
下面我们就来看一下AfxB eginThread函数的内部实现
//启动worke r线程CWinThread* AFXAPI AfxBegin Thread(AFX_THREADPROC pfnThrea dProc, LPVOID pParam, int nPri ority, UINT nStackSize, DWORD dwCreateFlags, LPSECURITY_ATTR IBUTES lpSecurityAttrs){#ifnde f _MT pfnThreadProc;
pParam;
nPriori ty;
nStackSize;
dwCreateFlags;
lpSec urityAttrs;
return NULL;
#else ASSERT(pfn ThreadProc != NULL);
CWinThread* pThread = DEBUG _NEW CWinThread(pfnThreadProc, pParam);
ASSERT_VALID (pThread);
if (!pT hread->
CreateThread(dwCreat eFlags|CREATE_SUSPENDED, nStac kSize, lpSecu rityAttrs)) { pThread->
Delete();
return NULL;
} VERIFY(pThr ead->
SetThreadPriority(nPri ority));
if (!(dwCreat eFlags &
CREATE_SUSPENDED) ) VERIFY(pThr ead->
ResumeThread() != (DWO RD)-1);
return pTh read;
#endif //!_MT)}
//启动UI 线程CWinThread* AFXAPI AfxBeginT hread(CRuntimeClass* pThreadCl ass, int nPriority, UINT nStac kSize, DWORD dwCreateFlags, LP SECURITY_ATTRIBUTES lpSecurity Attrs){#ifndef _MT pThr eadClass;
nPriority;
nStackSize;
dwCrea teFlags;
lpSecurityAttr s;
return NULL;
#els e ASSERT(pThreadClass ! = NULL);
ASSERT(pThread Class->
IsDerivedFrom(RUNTIM E_CLASS(CWinThread)));
CWinThread* pThread = (CWi nThread*)pThreadClass->
Crea teObject();
if (pThread == NULL) AfxTh rowMemoryException();
A SSERT_VALID(pThread);
pThread->
m_pThreadParams = NULL;
if (!pThread-& gt;
CreateThread(dwCreateFlags| CREATE_SUSPENDED, nStackSize, lpSecurityAttrs )) { pTh read->
Delete();
return NULL;
} VERIFY(pThread->
SetThrea dPriority(nPriority));
if (!(dwCreateFlags &
CREA TE_SUSPENDED)) VERIFY(pThread->
ResumeThrea d() != (DWORD)-1);
return pThread;
#endif //!_MT}< br>从上面的代码中可以看出AfxBeginThread所做 的事情主要有以下几点:
1.在heap中配置一个新的C WinThread对象(worker线程)代码如:CWinT hread* pThread = DEBUG_NEW CWi nThread(pfnThreadProc, pParam) ;
调用CRuntimeClass结构中的CreateObje ct函数创建CWinThread对象CWinThread* pThread = (CWinThread*)pThread Class->
CreateObject();
CRunt imeClass以及MFC相关类的内部实现,详情请参考《深入 浅出MFC》侯捷著
2.调用CWinThread::C reateThread()并设定属性,使线程以挂起状态产生p Thread->
CreateThread(dwCrea teFlags|CREATE_SUSPENDED, nSta ckSize,lpSecurityAttrs);
3. 设定线程的优先权pThread->
SetThreadP riority(nPriority);
4.调用CWi nThread::ResumeThreadpThread-& gt;
ResumeThread();
通过上面的说明,我想大家 对该函数到底在内部都做了什么,应该有一个初步的了解了!对于V C老手来说,这篇文章可能并没有什么可读之处,但是对于初学者来 说,还是有一定的价值的!总之,希望这篇文章能给各位一点点的帮 助!
WinThread* AfxBeginThread( AFX_THREADPROC pfnThreadProc,
返回值: 一个指向新线程的线程对象
pfnThreadProc : 线程的入口函数,声明一定要如下: UINT MyThreadFunction( LPVOID pParam );
pParam : 传递入线程的参数,注意它的类型为:LPVOID,所以我们可以传递一个结构体入线程.
nPriority : 线程的优先级,一般设置为 0 .让它和主线程具有共同的优先级.
nStackSize : 指定新创建的线程的栈的大小.如果为 0,新创建的线程具有和主线程一样的大小的栈
dwCreateFlags : 指定创建线程以后,线程有怎么样的标志.可以指定两个值:
CREATE_SUSPENDED : 线程创建以后,会处于挂起状态,真到调用: ResumeThread
0 : 创建线程后就开始运行.
lpSecurityAttrs : 指向一个 SECURITY_ATTRIBUTES 的结构体,用它来标志新创建线程的安全性.如果为 NULL ,
那么新创建的线程就具有和主线程一样的安全性.
如果要在线程内结束线程,可以在线程内调用 AfxEndThread.
结束线程的两种方式
面会详细的向你解释要结束线程的两种方式
当然你可以定义自己的认为合适的值来代表线程成功执行.在线程内调用AfxEndThread将会直接结束线程,此时线
程的一切资源都会被回收.
不管是工作者线程还是界面线程,如果你想在线程结束后得到它的确结果,那么你可以调用:
::GetExitCodeThread函数
函数功能描述:创建新的线程
函数原型:
CWinThread* AfxBeginThread( AFX_THREADPROC pfnThreadProc, LPVOID pParam, int nPriority =
THREAD_PRIORITY_NORMAL, UINT nStackSize = 0, 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:工作线程的函数指针,不可以为空。并且工作线程的函数必须如此声明:
pThreadClass: 从CWinThread类继承来的对象的RUNTIME_CLASS指针。
pParam: 传递给工作线程函数pfnThreadProc的参数。
nPriority: 线程的优先级。如果为0,则与创建它的线程优先级相同。可以通过参考Win32 Programmer’s
nStackSize: 以字节为单位指定新线程的堆栈大小。如果为0,则与创建它的线程的堆栈大小相同。
dwCreateFlags:指定一个额外的标志控制线程的产生。它可以包括下面两个值中的一个:
CREATE_SUSPENDED:以挂起模式开始线程,并且指定挂起次数.当调用ResumeThread时,这个
lpSecurityAttrs:指向SECURITY_ATTRIBUTES结构的指针,结构中指定了线程的安全属性。如果为NULL,则与
注释:
调用这个函数创建一个新的线程。第一种形式的AfxBeginThread创建一个工作线程;第二种形式创建一个用户
接口线程。
AfxBeginThread创建一个新的CWinThread对象,调用它的CreateThread函数开始执行线程并且返回指向线程的指
针。Checks are made throughout the procedure to make sure all objects are deallocated properly
should any part of the creation fail. 终止线程,可以在线程函数中调用AfxEndThread, 或者从工作线程
的函数中返回。
了解更多的有关AfxBeginThread的信息,可以参考文章 Multithreading: Creating Worker Threads 和
Multithreading: Creating User-Interface Threads in Visual C++ Programmer’s Guide.
参看:AfxGetThread
示例:
UINT
CWinThread
LPVOID
int
UINT
DWORD
LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL ;//与创建它的线程安全属性相同
pMyFirstWorker=AfxBeginThread( (AFX_THREADPROC)WorkForce, pParam, nPriority , nStackSize,
dwCreateFlags , lpSecurityAttrs);
pMySecondWorker=AfxBeginThread( (AFX_THREADPROC)WorkForce, pParam);//如果采用默认值
DWORD WINAPI WorkForce( LPVOID lpParameter
{
return 0;//什么不做
}
AfxBeginThread
用户界面线程和工作者线程都是由AfxBeginThread创建的。现在,考察该函数:MFC提供了两个重载版的AfxBeginThread,一个用于用户界面线程,另一个用于工作者线程,分别有如下的原型和过程:
用户界面线程的AfxBeginThread
用户界面线程的AfxBeginThread的原型如下:
CWinThread* AFXAPI AfxBeginThread(
CRuntimeClass* pThreadClass,
int nPriority,
UINT nStackSize,
DWORD dwCreateFlags,
LPSECURITY_ATTRIBUTES lpSecurityAttrs)
其中:
参数1是从CWinThread派生的RUNTIME_CLASS类;
参数2指定线程优先级,如果为0,则与创建该线程的线程相同;
参数3指定线程的堆栈大小,如果为0,则与创建该线程的线程相同;
参数4是一个创建标识,如果是CREATE_SUSPENDED,则在悬挂状态创建线程,在线程创建后线程挂起,否则线程在创建后开始线程的执行。
参数5表示线程的安全属性,NT下有用。
工作者线程的AfxBeginThread
工作者线程的AfxBeginThread的原型如下:
CWinThread* AFXAPI AfxBeginThread(
AFX_THREADPROC pfnThreadProc,
LPVOID pParam,
int nPriority,
UINT nStackSize,
DWORD dwCreateFlags,
LPSECURITY_ATTRIBUTES lpSecurityAttrs)
其中:
参数1 线程的入口函数,声明一定要如下: UINT MyThreadFunction( LPVOID pParam );
参数2 传递入线程的参数,注意它的类型为:LPVOID,所以我们可以传递一个结构体入线程.
参数3、4、5分别指定线程的优先级、堆栈大小、创建标识、安全属性,含义同用户界面线程。
附录A:
结束线程的两种方式
当你在后台用线程来打印一些图形时.有时在打印一部分后,你希望可以停下来,那么此如何让线程停止呢.下
面会详细的向你解释要结束线程的两种方式
1 : 这是最简单的方式,也就是让线程函数执行完成,此时线程正常结束.它会返回一个值,一般0是成功结束,
当然你可以定义自己的认为合适的值来代表线程成功执行.在线程内调用AfxEndThread将会直接结束线程,此时线程的一切资源都会被回收.
2 : 如果你想让别一个线程B来结束线程A,那么,你就需要在这两个线程中传递信息.不管是工作者线程还是界面线程,如果你想在线程结束后得到它的确结果,那么你可以调用 ::GetExitCodeThread函数
还是老师的那个项目,以前由于计算量太大,导致程序经常出现假死的现象,因为程序只有一个线程,该线程主要用于处理计算上了,而对于消息队列的响应被忽略了。因此解决的办法就是用两个线程,一个线程用于计算,一个线程用于处理消息。
到网上找了一些资料,发现在MFC中把线程分为两类,一类为界面线程,一类为工作线程。两者的区别在于前都能够处理消息响应,而后者则不能。对于该项目来说,只要把计算的过程放到一个工作线程里来进行就可以了。
现在先试一下,我新建了一个对话框,上面添加两个按钮,一个是start 一个是dialog。前者用于开始计算,而后者则弹出一个消息框。然后向该对话框里面添加一个死循环的函数
UINT CMultithreadDlg::jisuan(LPVOID lpParam)
{
int i = 1;
for (;;)
{
i+=i;
}
return 0;
}
然后在start按钮的响应函数上添加上jisuan(NULL);即可,现在运行程序,按下start按钮后,可以看到CPU使用率涨到了100%,这 个时候再按dialog按钮无反应,拖动关闭窗口均无效。这就是前面提到的假死现象(实际上是真死,因为死循环了,如果不是死循环,而只是计算量太大才是 假死)。
下面用多线程的方法来解决,在start按钮的响应函数改为
CWinThread* mythread = AfxBeginThread(
jisuan,
NULL,
THREAD_PRIORITY_NORMAL,
0,
0,
NULL
);
运行,结果发现有错error C2665: 'AfxBeginThread' : none of the 2 overloads can convert parameter 1 from type 'unsigned int (void *)' Generating Code...
我就纳闷了,函数指针是对的啊,原来 线程函数可以且必须是全局函数或者是静态成员函数。
所以我们在线程函数的声明中改为 static UINT jisuan(LPVOID lpParam);即可,然后运行程序,这时点击start,待CPU涨至100%后,点击dialog,弹出对话框了,拖动、关闭窗口均没问题了。
其实上面的那个AfxBeginThread,除前面两个参数外,后面的都是默认参数,可以省略。而必须有的这两个参数,一个是线程函数的指针,一个是传 递给这个函数的参数。实际中我们经常这样用 AfxBeginThread(ThreadProc,this);//把this传过去,就可以调用类的成员了. 这样线程函数就可以使用和操作类的成员了。千万要注意线程函数是静态类函数成员。
线程是创建了,但是如果中途要暂停该怎么做呢?
我们在创建线程的时候获得了一个CWinThread的指针,这是一个指向线程对象的指针,CWinThread类里面就有暂停与恢复的函数,下面我就演示一下。
在原来的程序上进行改动。向对话框类里面添加一个CWinThread* 的成员变量,不用初始化为NULL,这样会报错的,因为它只能通过AfxBeginThread函数获得。把start里面的声明去掉。
然后添加一个 pause 按钮向其响应函数里面添加代码 mythread->SuspendThread(); 再添加一个 resume按钮,向其响应函数里面添加 mythread->ResumeThread();
再运行程序,我们start之后,按下pause可以看到CPU恢复正常,然后resume,CPU又涨上去了,到此证明一切操作正常。
两个的实质都是一样的,不过AfxBeginThread返回一个CWinThread的指针,就是说他会new一个CWinThread对象,而且这个对象是自动删除的(在线程 运行 结束时),给我们带来的不便就是无法获得它的状态,因为随时都有可能这个指针指向的是一个已经无效的 内存 区域,所以使用时(如果需要了解它的运行状况的话)首先CREATE_SUSPENDED让他挂起,然后m_bAutoDelete=FALSE,接着才ResumeThread,最后不要了delete那个指针。 CreatThread就方便多了,它返回的是一个句柄,如果你不使用CloseHandle的话就可以通过他 安全 的了解线程状态,最后不要的时候CloseHandle,Windows才会释放资源,所以我一般使用CreatThread, 方便 。
如果用MFC编程,不要用CreateThread,如果只是使用Runtime Library,用_BegingThread,总之,不要轻易使用CreateThread。这是因为在MFC和RTL中的函数有可能会用到些它们所封装的公用变量,也就是说AfxBeginThread和_BeginThread都有自己的启动代码是CreateThread所没有的。在用CreateThread所创建的线程中使用MFC的类和RTL函数就有可能出现问题。如果你是用汇编编写win32程序并且在线程函数中也不调用MFC和RTL的函数,那用CreateThread就没问题,或者你虽然是用C写线程函数,但你很小心没调用RTL函数也不会有问题。
CreateThread是由操作系统提供的接口,而AfxBeginThread和_BeginThread则是编译器对它的封装。
在可能的情况下,不要调用_beginthread,而应该调用_beginthreadex。以及对应的_endthreadex。这都是C++运行期函数。但是使用_beginthread,无法创建带有安全属性的新线程,无法创建暂停的线程,也无法获得线程ID,_endthread的情况类似,它不带参数,
这意味这线程的退出代码必须硬编码为0。这两个函数在_beginthreadex和_endthreadex中进行调用。CreateThread不要进行直接调用。
写的有点乱,呵呵
AfxBeginThread
用户界面线程和工作者线程都是由AfxBeginThread创建的。现在,考察该函数:MFC提供了两个重载版的AfxBeginThread,一个用于用户界面线程,另一个用于工作者线程,分别有如下的原型和过程:
用户界面线程的AfxBeginThread
用户界面线程的AfxBeginThread的原型如下:
CWinThread* AFXAPI AfxBeginThread(
CRuntimeClass* pThreadClass,
int nPriority,
UINT nStackSize,
DWORD dwCreateFlags,
LPSECURITY_ATTRIBUTES lpSecurityAttrs)
其中:
参数1是从CWinThread派生的RUNTIME_CLASS类;
参数2指定线程优先级,如果为0,则与创建该线程的线程相同;
参数3指定线程的堆栈大小,如果为0,则与创建该线程的线程相同;
参数4是一个创建标识,如果是CREATE_SUSPENDED,则在悬挂状态创建线程,在线程创建后线程挂起,否则线程在创建后开始线程的执行。
参数5表示线程的安全属性,NT下有用。
工作者线程的AfxBeginThread
工作者线程的AfxBeginThread的原型如下:
CWinThread* AFXAPI AfxBeginThread(
AFX_THREADPROC pfnThreadProc,
LPVOID pParam,
int nPriority,
UINT nStackSize,
DWORD dwCreateFlags,
LPSECURITY_ATTRIBUTES lpSecurityAttrs)
其中:
参数1 线程的入口函数,声明一定要如下: UINT MyThreadFunction( LPVOID pParam );
参数2 传递入线程的参数,注意它的类型为:LPVOID,所以我们可以传递一个结构体入线程.
参数3、4、5分别指定线程的优先级、堆栈大小、创建标识、安全属性,含义同用户界面线程。
附录A:
结束线程的两种方式
当你在后台用线程来打印一些图形时.有时在打印一部分后,你希望可以停下来,那么此如何让线程停止呢.下
面会详细的向你解释要结束线程的两种方式
1 : 这是最简单的方式,也就是让线程函数执行完成,此时线程正常结束.它会返回一个值,一般0是成功结束,
当然你可以定义自己的认为合适的值来代表线程成功执行.在线程内调用AfxEndThread将会直接结束线程,此时线程的一切资源都会被回收.
2 : 如果你想让别一个线程B来结束线程A,那么,你就需要在这两个线程中传递信息.不管是工作者线程还是界面线程,如果你想在线程结束后得到它的确结果,那么你可以调用 ::GetExitCodeThread函数
还是老师的那个项目,以前由于计算量太大,导致程序经常出现假死的现象,因为程序只有一个线程,该线程主要用于处理计算上了,而对于消息队列的响应被忽略了。因此解决的办法就是用两个线程,一个线程用于计算,一个线程用于处理消息。
到网上找了一些资料,发现在MFC中把线程分为两类,一类为界面线程,一类为工作线程。两者的区别在于前都能够处理消息响应,而后者则不能。对于该项目来说,只要把计算的过程放到一个工作线程里来进行就可以了。
现在先试一下,我新建了一个对话框,上面添加两个按钮,一个是start 一个是dialog。前者用于开始计算,而后者则弹出一个消息框。然后向该对话框里面添加一个死循环的函数
UINT CMultithreadDlg::jisuan(LPVOID lpParam)
{
int i = 1;
for (;;)
{
i+=i;
}
return 0;
}
然后在start按钮的响应函数上添加上jisuan(NULL);即可,现在运行程序,按下start按钮后,可以看到CPU使用率涨到了100%,这个时候再按dialog按钮无反应,拖动关闭窗口均无效。这就是前面提到的假死现象(实际上是真死,因为死循环了,如果不是死循环,而只是计算量太大才是假死)。
下面用多线程的方法来解决,在start按钮的响应函数改为
CWinThread* mythread = AfxBeginThread(
jisuan,
NULL,
THREAD_PRIORITY_NORMAL,
0,
0,
NULL
);
运行,结果发现有错error C2665: 'AfxBeginThread' : none of the 2 overloads can convert parameter 1 from type 'unsigned int (void *)' Generating Code...
我就纳闷了,函数指针是对的啊,原来 线程函数可以且必须是全局函数或者是静态成员函数。
所以我们在线程函数的声明中改为 static UINT jisuan(LPVOID lpParam);即可,然后运行程序,这时点击start,待CPU涨至100%后,点击dialog,弹出对话框了,拖动、关闭窗口均没问题了。
其实上面的那个AfxBeginThread,除前面两个参数外,后面的都是默认参数,可以省略。而必须有的这两个参数,一个是线程函数的指针,一个是传递给这个函数的参数。实际中我们经常这样用 AfxBeginThread(ThreadProc,this);//把this传过去,就可以调用类的成员了. 这样线程函数就可以使用和操作类的成员了。千万要注意线程函数是静态类函数成员。
线程是创建了,但是如果中途要暂停该怎么做呢?
我们在创建线程的时候获得了一个CWinThread的指针,这是一个指向线程对象的指针,CWinThread类里面就有暂停与恢复的函数,下面我就演示一下。
在原来的程序上进行改动。向对话框类里面添加一个CWinThread* 的成员变量,不用初始化为NULL,这样会报错的,因为它只能通过AfxBeginThread函数获得。把start里面的声明去掉。
然后添加一个 pause 按钮向其响应函数里面添加代码 mythread->SuspendThread(); 再添加一个 resume按钮,向其响应函数里面添加 mythread->ResumeThread();
再运行程序,我们start之后,按下pause可以看到CPU恢复正常,然后resume,CPU又涨上去了,到此证明一切操作正常。
在选择启动线程的方法时,(_beginthreadex、 CreateThread 、 AfxBeginThread )
参考候捷的《Win32多线程程序设计》遇到的问题
但看了候更糊涂了
问题如下:
1:《Win32多线程程序设计》第219页中间:不要在一个MFC程序中使用_beginthreadex()或 CreateThread ()。
我的程序中使用的CString/CArray等MFC类,是否我只能使用 AfxBeginThread ()?
2:我的程序使用了一个库,里面用到了很多memcpy/memmove/fopen/fread等函数。
这些函数应该算是CRun-timeLibrary中的函数吧?
我创建的线程中调用了这个库中的函数,是不是使用_beginthreadex()才是安全的?
3:《Win32多线程程序设计》第228页中间:如果主线程以为的任何线程进行了以下操作,你就应该使用多线程版的Cruntimelibrary,并使用_beginthreadex()和_endthreadex():在C++程序中使用了new和delete。
我的问题是,在C++程序中,几乎都要用到new和delete,难道只有使用_beginthreadex()吗?
回复人:vcforever(霓裳羽衣)
1、不要在一个MFC程序中使用_beginthreadex()或 CreateThread ()。
这句话的意思是由于 AfxBeginThread ()是MFC封装的启动线程的函数,里面包含了很多和MFC相关的启动信息,而且封装了一些常用的操作,使用起来也比较简便。而用另外两个函数就需要程序员对类型,安全性检查进行更多的思考!
2、用_beginthreadex()函数应该是最佳选择。
因为_beginthreadex()函数是CRun-timeLibrary中的函数,函数的参数和数据类型都是CRun-timeLibrary中的类型,这样在启动线程时就不需要进行Windows数据类型和CRun-timeLibrary中的数据类型之间的转化。减低了线程启动时的资源消耗和时间的消耗!
3、在C++程序中,几乎都要用到new和delete,难道只有使用_beginthreadex()?
不,因为MFC也是C++类库(只不过是Microsoft的C++类库,不是标准的C++类库),在MFC中也封装了new和delete两中运算符,所以用到new和delete的地方不一定非要使用_beginthreadex()函数,用其他两个函数都可以!
其实在程序中使用上面的哪个函数并不是绝对的,书的作者只不过是提了一个更佳的搭配方法,我在MFC程序中也经常使用_beginthreadex()和 CreateThread ()这两个函数,运行的效果也没有多大的区别,有的时候只是需要你额外的进行一些类型检查和其他的一些转化操作,其余没有其他不妥!
上面是俺的一些理解,和在工作中的一点感觉!
//这是一个多线程控制进度条的例子,很简单,但是对于初学者掌握多线程的实现方法是非常硬手的。
//重点是AfxBeginThread的用法,
//
//
// MultiThread5Dlg.cpp : implementation file
//
#include "stdafx.h"
#include "MultiThread5.h"
#include "MultiThread5Dlg.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/
// CAboutDlg dialog used for App About
struct threadInfo
{
UINT nMilliSecond;
CProgressCtrl* pctrlProgress;//进程指针
};//结构体
threadInfo Info;
threadInfo Info2;//此处增添新进程的定义
class CAboutDlg : public CDialog
{
public:
CAboutDlg();
// Dialog Data
//{{AFX_DATA(CAboutDlg)
enum { IDD = IDD_ABOUTBOX };
//}}AFX_DATA
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CAboutDlg)
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
//}}AFX_VIRTUAL
// Implementation
protected:
//{{AFX_MSG(CAboutDlg)
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{
//{{AFX_DATA_INIT(CAboutDlg)
//}}AFX_DATA_INIT
}
void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CAboutDlg)
//}}AFX_DATA_MAP
}
BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
//{{AFX_MSG_MAP(CAboutDlg)
// No message handlers
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/
// CMultiThread5Dlg dialog
CMultiThread5Dlg::CMultiThread5Dlg(CWnd* pParent /*=NULL*/)
: CDialog(CMultiThread5Dlg::IDD, pParent)
{
//{{AFX_DATA_INIT(CMultiThread5Dlg)
m_nMilliSecond = 0;
//}}AFX_DATA_INIT
// Note that LoadIcon does not require a subsequent DestroyIcon in Win32
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}
void CMultiThread5Dlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CMultiThread5Dlg)
DDX_Control(pDX, IDC_PROGRESS1, m_ctrlProgress);
DDX_Control(pDX, IDC_PROGRESS2, m_ctrlProgress2);
DDX_Text(pDX, IDC_MILLISECOND, m_nMilliSecond);
//}}AFX_DATA_MAP
}
BEGIN_MESSAGE_MAP(CMultiThread5Dlg, CDialog)
//{{AFX_MSG_MAP(CMultiThread5Dlg)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_BN_CLICKED(IDC_START, OnStart)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/
// CMultiThread5Dlg message handlers
BOOL CMultiThread5Dlg::OnInitDialog()
{
CDialog::OnInitDialog();
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX < 0xF000);
CMenu* pSysMenu = GetSystemMenu(FALSE);
if (pSysMenu != NULL)
{
CString strAboutMenu;
strAboutMenu.LoadString(IDS_ABOUTBOX);
if (!strAboutMenu.IsEmpty())
{
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
}
}
SetIcon(m_hIcon, TRUE); // Set big icon
SetIcon(m_hIcon, FALSE); // Set small icon
// TODO: Add extra initialization here
m_ctrlProgress.SetRange(0,99);
m_ctrlProgress2.SetRange(0,99); //确定进度条的范围
m_nMilliSecond=10;
UpdateData(FALSE);
return TRUE; // return TRUE unless you set the focus to a control
}
void CMultiThread5Dlg::OnSysCommand(UINT nID, LPARAM lParam)
{
if ((nID & 0xFFF0) == IDM_ABOUTBOX)
{
CAboutDlg dlgAbout;
dlgAbout.DoModal();
}
else
{
CDialog::OnSysCommand(nID, lParam);
}
}
void CMultiThread5Dlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this); // device context for painting
SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);
// Center icon in client rectangle
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;
// Draw the icon
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialog::OnPaint();
}
}
HCURSOR CMultiThread5Dlg::OnQueryDragIcon()
{
return (HCURSOR) m_hIcon;
}
void CMultiThread5Dlg::OnStart()
{
UpdateData(TRUE);
Info.nMilliSecond=m_nMilliSecond;
Info.pctrlProgress=&m_ctrlProgress;
pThread=AfxBeginThread(ThreadFunc,&Info);
第二线程开始
Info2.nMilliSecond=m_nMilliSecond;
Info2.pctrlProgress=&m_ctrlProgress2;
pThread=AfxBeginThread(ThreadFunc,&Info2); // 此处也可以用thradfunc2
}
UINT ThreadFunc(LPVOID lpParam)
{
threadInfo* pInfo=(threadInfo*)lpParam;
for(int i=0;i<100;i++)
{
int nTemp=pInfo->nMilliSecond;
pInfo->pctrlProgress->SetPos(i);
Sleep(nTemp);
}
return 0;
}// 线程的处理方法
UINT ThreadFunc2(LPVOID lpParam)
{
threadInfo* pInfo=(threadInfo*)lpParam;
for(int i=0;i<100;i++)
{
int nTemp=pInfo->nMilliSecond;
pInfo->pctrlProgress->SetPos(i);
Sleep(nTemp);
}
return 0;
}///也可以用上面的那个个函数实现
AfxBeginThread的基本用法
CWinThread* AfxBeginThread( AFX_THREADPROC pfnThreadProc,
LPVOID pParam,
int nPriority = THREAD_PRIORITY_NORMAL,
UNT nStackSize = 0,
DWORD dwCreateFlags = 0,
LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL
);//用于创建工作者线程
返回值: 一个指向新线程的线程对象
pfnThreadProc : 线程的入口函数,声明一定要如下: UINT MyThreadFunction( LPVOID pParam );
pParam : 传递入线程的参数,注意它的类型为:LPVOID,所以我们可以传递一个结构体入线程.
nPriority : 线程的优先级,一般设置为 0 .让它和主线程具有共同的优先级.
nStackSize : 指定新创建的线程的栈的大小.如果为 0,新创建的线程具有和主线程一样的大小的栈
dwCreateFlags : 指定创建线程以后,线程有怎么样的标志.可以指定两个值:
CREATE_SUSPENDED : 线程创建以后,会处于挂起状态,真到调用: ResumeThread
0 : 创建线程后就开始运行.
lpSecurityAttrs : 指向一个 SECURITY_ATTRIBUTES 的结构体,用它来标志新创建线程的安全性.如果为 NULL ,
那么新创建的线程就具有和主线程一样的安全性.
如果要在线程内结束线程,可以在线程内调用 AfxEndThread.
结束线程的两种方式
当你在后台用线程来打印一些图形时.有时在打印一部分后,你希望可以停下来,那么此如何让线程停止呢.下
面会详细的向你解释要结束线程的两种方式
1 : 这是最简单的方式,也就是让线程函数执行完成,此时线程正常结束.它会返回一个值,一般0是成功结束,
当然你可以定义自己的认为合适的值来代表线程成功执行.在线程内调用AfxEndThread将会直接结束线程,此时线
程的一切资源都会被回收.
2 : 如果你想让别一个线程B来结束线程A,那么,你就需要在这两个线程中传递信息.
不管是工作者线程还是界面线程,如果你想在线程结束后得到它的确结果,那么你可以调用:
::GetExitCodeThread函数