AfxBeginThread函数

2023-12-10 00:08
文章标签 函数 afxbeginthread

本文主要是介绍AfxBeginThread函数,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

在进行多线程程序设计的时候,我们经常用到AfxBeginThread函数来启动一条线程
该函数使用起来非常的简单方便,其定义如下

Worker线程:

CWinThread* AfxBeginThread(
   AFX_THREADPROC pfnThreadProc,//线程函数地址
   LPVOID pParam,//线程参数
   int nPriority = THREAD_PRIORITY_NORMAL,//线程优先级
   UINT nStackSize = 0,//线程堆栈大小,默认为1M
   DWORD dwCreateFlags = 0,//
   LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL 
);

 

UI线程

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();

 

 

近日在网上看到很多人问及如何关闭一下线程,但是我看网上给出的并不详细,而且有些方法还是错误的。小弟在此拙作一篇,不谈别的,只谈及如何正确的关闭MFC的线程,至于Win32和C RunTime的线程暂不涉及。

一.关于MFC的线程

  1.MFC的线程有两种,一种称为Work线程,一种称为UI线程。一般情况下Work线程与UI线程的区别主要在于UI线程有消息队列(并不是有没有界面,这点要注意,UI线程也是可以没有界面的)。

  2.创建这两种线程的区别也不大,可以从创建函数看出。

[cpp] view plain copy print ?
  1. // Work线程   
  2. CWinThread* AfxBeginThread(  
  3.       AFX_THREADPROC pfnThreadProc,  
  4.       LPVOID pParam,  
  5.       int nPriority = THREAD_PRIORITY_NORMAL,  
  6.       UINT nStackSize = 0,  
  7.       DWORD dwCreateFlags = 0,  
  8.       LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL  
  9.      );  
  10.   
  11. // UI线程   
  12. CWinThread* AfxBeginThread(  
  13.       CRuntimeClass* pThreadClass,  
  14.       int nPriority = THREAD_PRIORITY_NORMAL,  
  15.       UINT nStackSize = 0,  
  16.       DWORD dwCreateFlags = 0,  
  17.       LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL  
  18.      );  

关于函数的具体使用,请查阅MSDN,这里不涉及。

二. 结束线程前的注意事项

  在结束一个线程前,只有一点要注意,那就是m_bAutoDelete 的状态.它可以在线程终止时,指定是否自动删除CWinThread对象

[c-sharp] view plain copy print ?
  1. m_bAutoDelete = FALSE; // 表示你自己管理 CWind 对象,包括它的清理  
  2.   
  3. m_bAutoDelete = TRUE;  // 默认值, 系统会自己清理 CWind 对象  

m_bAutoDelete = TRUE; 系统自己清理CWind对象,当然还包括CloseHandle(),ExitInstance()等等一堆函数的调用。

m_bAutoDelete = FALSE; 那么就一定要记得自己在用完后调用delete删除创建线程的对象,这一点极为重要,因为不调用delete一定会有内存泄漏问题

总之m_bAutoDelete 的值对结束工作是很重要的,这点一定要注意。

三.正确的结束一个Work线程

  因为Work线程是一个全局函数,或者是一个Static函数,所以它的运行完成也就是它的正常退出了。(什么?不明白,示例代码如下)

  1.情况一:

[c-sharp] view plain copy print ?
  1. UINT WorkFunc(LPVOID pParam)  
  2. {  
  3.   // 工作   
  4.   ......  
  5.    return 0;  // 就算正常退出了,简单吧  
  6. }  

  2.情况二:

Work线程是个死循环或一时半会儿出不来,这时要主线程要发个消息给Work线程,让他退出。

[cpp] view plain copy print ?
  1. UINT WorkFunc(LPVOID pParam)  
  2. {  
  3.   for(;;)  
  4.   {  
  5.    // ...   
  6.   if( WAIT_OBJECT_0 == WaitForSingleObject(m_hThread, INFINITE)} // 收到激发态的消息  
  7.     {  
  8.       return 0;//正常退出  
  9.      }  
  10.   
  11.  }//end for   
  12.     
  13.  return 0;  
  14. }  

关于主线程发一个激发态的消息给Work线程,有多种方法,如在主线程里调用SetEvent()等等,你想用什么都行,但是最好不要在Work线程里用Busy loop的方法。至于为什么,请参阅《Win32多线程程序设计》上面的论述。

四.正确结束一个UI线程

  因为UI线程有消息队列,所以结束一个UI线程最好的方法是发一个WM_QUIT消息给消息队列,方法很多如:PostQuitMessage(),PostThreadMessage()等等。但是发出消息后最好等待看UI线程是否已经退出(很多人都没有提及这一点,但是实际工作中发现,加上这一点是多么的重要)

[c-sharp] view plain copy print ?
  1. // 主线程结束UI线程的代码   
  2. if(pThread)   
  3. {  
  4.   // 1. 发一个WM_QUIT 消息结 UI 线程   
  5.   pThread->PostThreadMessage(WM_QUIT, NULL, NULL);  
  6.                   
  7.  // 2. 等待 UI 线程正常退出   
  8.  if (WAIT_OBJECT_0 == WaitForSingleObject(pThread->m_hThread, INFINITE))  
  9.  {  
  10.     // 3. 删除 UI 线程对象,只有当你设置了m_bAutoDelete = FALSE; 时才调用  
  11.   delete   pThread;   
  12.  }  
  13.   
  14. }  

五.关于几个问题的解答

1.问:为什么我的UI线程没有调用ExitInstance()?

  答:最大的可能是你的WM_QUIT消息没有通知到UI线程。为了保险期间最好调用PostThreadMessage(),这样可以指定线程的ID。当然如果你对消息比较熟悉的话,也可以抛一个消息到最顶层。

2.问:为什么我的UI线程没有调用析构函数?

  答:检查看你的m_bAutoDelete = FALSE,如果是的话,那么看你的线程对象是否已经delete了。一般情况下调用delete会调用析构函数

3.问:在UI线程中没有调用WaitForSingleObject(),会怎么样?

  答:我们知道在PostMessage()之后,函数会马上返回,如查没有wait...(),那么紧接着就调用了delete,很有可能对象做的退出操作过程还没有完成时,又把对象delete掉了,结果还是没有正常结束。(注:WM_QUIT消息之后会触发一堆函数,这个时间是不定的,所以最好Wait...才是正道。)

六.最后不愿提的函数

  几乎每本讲线程的书都会提到下面的函数:

  void AfxEndThread(UINT nExitCode);

  TerminateThread();

  ......还有其它的一些极端的函数

我的观点是:最好不要使用,除非你知道要发生什么!

 

这篇关于AfxBeginThread函数的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/475493

相关文章

Python容器转换与共有函数举例详解

《Python容器转换与共有函数举例详解》Python容器是Python编程语言中非常基础且重要的概念,它们提供了数据的存储和组织方式,下面:本文主要介绍Python容器转换与共有函数的相关资料,... 目录python容器转换与共有函数详解一、容器类型概览二、容器类型转换1. 基本容器转换2. 高级转换示

pandas使用apply函数给表格同时添加多列

《pandas使用apply函数给表格同时添加多列》本文介绍了利用Pandas的apply函数在DataFrame中同时添加多列,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习... 目录一、Pandas使用apply函数给表格同时添加多列二、应用示例一、Pandas使用apply函

Python中Namespace()函数详解

《Python中Namespace()函数详解》Namespace是argparse模块提供的一个类,用于创建命名空间对象,它允许通过点操作符访问数据,比字典更易读,在深度学习项目中常用于加载配置、命... 目录1. 为什么使用 Namespace?2. Namespace 的本质是什么?3. Namesp

MySQL中如何求平均值常见实例(AVG函数详解)

《MySQL中如何求平均值常见实例(AVG函数详解)》MySQLavg()是一个聚合函数,用于返回各种记录中表达式的平均值,:本文主要介绍MySQL中用AVG函数如何求平均值的相关资料,文中通过代... 目录前言一、基本语法二、示例讲解1. 计算全表平均分2. 计算某门课程的平均分(例如:Math)三、结合

Python函数作用域与闭包举例深度解析

《Python函数作用域与闭包举例深度解析》Python函数的作用域规则和闭包是编程中的关键概念,它们决定了变量的访问和生命周期,:本文主要介绍Python函数作用域与闭包的相关资料,文中通过代码... 目录1. 基础作用域访问示例1:访问全局变量示例2:访问外层函数变量2. 闭包基础示例3:简单闭包示例4

Python中isinstance()函数原理解释及详细用法示例

《Python中isinstance()函数原理解释及详细用法示例》isinstance()是Python内置的一个非常有用的函数,用于检查一个对象是否属于指定的类型或类型元组中的某一个类型,它是Py... 目录python中isinstance()函数原理解释及详细用法指南一、isinstance()函数

python中的高阶函数示例详解

《python中的高阶函数示例详解》在Python中,高阶函数是指接受函数作为参数或返回函数作为结果的函数,下面:本文主要介绍python中高阶函数的相关资料,文中通过代码介绍的非常详细,需要的朋... 目录1.定义2.map函数3.filter函数4.reduce函数5.sorted函数6.自定义高阶函数

Python中的sort方法、sorted函数与lambda表达式及用法详解

《Python中的sort方法、sorted函数与lambda表达式及用法详解》文章对比了Python中list.sort()与sorted()函数的区别,指出sort()原地排序返回None,sor... 目录1. sort()方法1.1 sort()方法1.2 基本语法和参数A. reverse参数B.

Python函数的基本用法、返回值特性、全局变量修改及异常处理技巧

《Python函数的基本用法、返回值特性、全局变量修改及异常处理技巧》本文将通过实际代码示例,深入讲解Python函数的基本用法、返回值特性、全局变量修改以及异常处理技巧,感兴趣的朋友跟随小编一起看看... 目录一、python函数定义与调用1.1 基本函数定义1.2 函数调用二、函数返回值详解2.1 有返

Python Excel 通用筛选函数的实现

《PythonExcel通用筛选函数的实现》本文主要介绍了PythonExcel通用筛选函数的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着... 目录案例目的示例数据假定数据来源是字典优化:通用CSV数据处理函数使用说明使用示例注意事项案例目的第一