Windows API 多线程 CreateThread、_beginthread、_beginthreadex、AfxBeginThread

新博客地址: vonsdite.cn

新博客地址: vonsdite.cn

文章目录

  • 新博客地址: vonsdite.cn
  • 新博客地址: vonsdite.cn
    • 1. 概述
    • 1. 编程的时候如何选择各个函数
    • 2. 尽量不调用`CreateThread`
    • 3. 注意
    • 4. `_beginthreadex`用法
    • 5.示例

1. 概述

  • CreateThread: Windows的API函数(SDK函数的标准形式,直截了当的创建方式,任何场合都可以使用),提供操作系统级别的创建线程的操作,且仅限于工作者线程(一般不建议使用

  • _beginthread_beginthreadex

    • MS对C Runtime库的扩展SDK函数,首先针对C Runtime库做了一些初始化的工作,以保证C Runtime库工作正常,
    • 然后,调用CreateThread真正创建线程。
    • _beginthread_beginthreadex的功能子集
    • 虽然_beginthread内部是调用_beginthreadex,但它屏蔽了安全特性这样的功能
      • 例如,如果使用_beginthread,就无法创建带有安全属性的新线程,无法创建暂停的线程,也无法获得线程的ID值。
    • _beginthreadCreateThread不是同等级别,_beginthreadexCreateThread在功能上完全可替代
  • AfxBeginThread:MFC中线程创建的MFC函数,它简化了操作或让线程能够响应消息,即可用于界面线程,也可以用于工作者线程,但要注意尽量不要在一个MFC程序中使用_beginthreadex()或CreateThread()。

  • AfxBeginThread_beginThread_beginThreadex实际上是编译器对CreateThread的封装

1. 编程的时候如何选择各个函数

  • MFC程序选择AfxBeginThread

  • 如果不使用Microsoft的Visual C++编译器,你的编译器供应商有它自己的CreateThred替代函数

2. 尽量不调用CreateThread

  • 尽量不要调用CreateThread。相反,应该使用Visual C++运行期库函数_beginthreadex,原因如下:

    • 考虑标准C运行时库的一些变量和函数,如errno,这是一个全局变量。全局变量用于多线程会出现资源竞争问题,需要进行同步互斥。故必须存在一种机制,使得每个线程能够引用它自己的errno变量,又不触及另一线程的errno变量,_beginthreadex就为每个线程分配自己的tiddata内存结构:
      • 该结构保存了许多像errno这样的变量和函数的值、地址。
      • 通过线程局部存储将tiddata与线程联系起来。具体实现在Threadex.c中有。
      • 结束线程使用函数_endthreadex函数,释放掉线程的tiddata数据块。
    • (以下主要介绍CreateThread会导致内存泄漏CRT的函数库在线程出现之前就已经存在,所以原有的CRT不能真正支持线程,这导致我们在编程的时候有了CRT库的选择, 在MSDN中查阅CRT的函数时都有:
      Libraries   
      LIBC.LIB   Single   thread   static   library,   retail   version     
      LIBCMT.LIB   Multithread   static   library,   retail   version     
      MSVCRT.LIB   Import   library   for   MSVCRT.DLL,   retail   version     
    
    • 这也导致了许多CRT的函数在多线程的情况下必须有特殊的支持,不能简单的使用CreateThread就行。 大多的CRT函数都可以在CreateThread线程中使用,看资料说只有signal()函数不可以,会导致进程终止。但其他大多数函数可以用并不是说没有问题, 有些CRT的函数malloc(), fopen(), _open(), strtok(), ctime(), 或localtime()等函数需要专门的线程局部存储的数据块,这个数据块通常需要在创建线程的时候就建立,如果使用CreateThread,这个数据块就没有建立,在这样的线程中还是可以使用这些函数而且没有出错,实际上函数发现这个数据块的指针为空时,会自己建立一个,然后将其与线程联系在一起,这意味着如果你用CreateThread来创建线程,然后使用这样的函数,会有一块内存在不知不觉中创建,遗憾的是,这些函数并不将其删除,而CreateThreadExitThread也无法知道这件事,于是就会有Memory leak,在线程频繁启动的软件中(比如某些服务器软件),迟早会让系统的内存资源耗尽! _beginthreadex_endthreadex就对这个内存块做了处理,所以没有问题。

    • 如果在除主线程之外的任何线程中进行一下操作,你就应该使用多线程版本的C runtime library,并使用_beginthreadex_endthreadex

    1. 使用malloc()free(),或是newdelete
    2. 使用stdio.hio.h里面声明的任何函数
    3. 使用浮点变量或浮点运算函数
    4. 调用任何一个使用了静态缓冲区的runtime函数,比如:asctime(),strtok()rand()

    Handle的问题,_beginthread的对应函数_endthread自动的调用了CloseHandle,而_beginthreadex的对应函数_endthreadex则没有,所以CloseHandle无论如何都是要调用的不过_endthread可以帮你执行自己不必写,其他两种就需要自己写!(Jeffrey Richter强烈推荐尽量不用显式的终止函数,用自然退出的方式,自然退出当然就一定要自己写CloseHandle)

3. 注意

1)C++主线程的终止,同时也会终止所有主线程创建的子线程,不管子线程有没有执行完毕。所以如果不调用WaitForSingleObject,则子线程可能并没有执行完毕或根本没有执行。
2)如果某线程挂起,然后有调用WaitForSingleObject等待该线程,就会导致死锁。所以上面的代码如果不调用resumethread,则会死锁。

4. _beginthreadex用法

  • 头文件: process.h

  • 函数原型:

unsigned long _beginthreadex( 
	void *security, 
	unsigned stack_size, 
	unsigned ( __stdcall *start_address )( void * ), 
	void *arglist, 
	unsigned initflag, 
	unsigned *thrdaddr 
);

//第1个参数:安全属性,NULL为默认安全属性 
//第2个参数:指定线程堆栈的大小。如果为0,则线程堆栈大小和创建它的线程的相同。一般用0 
//第3个参数:指定线程函数的地址,也就是线程调用执行的函数地址(用函数名称即可,函数名称就表示地址,注意的是函数访问方式一定是__stdcall,函数返回值一定是unsigned,函数参数一定是void*) 
//第4个参数:传递给线程的参数的指针,可以通过传入对象的指针,在线程函数中再转化为对应类的指针 
//第5个参数:线程初始状态,0:立即运行;CREATE_SUSPEND:悬挂(如果出事状态定义为悬挂,就要调用ResumeThread(HANDLE) 来激活线程的运行) 
//第6个参数:用于记录线程ID的地址

5.示例

  • 示例1:
#include 

#include 

#include 

#include 

using namespace std;

 

class ThreadX{

private:

    int loopStart;

    int loopEnd;

    int dispFrequency;

 

public:

    string threadName;

 

    ThreadX(int startVal, int endVal, int frequency){

        this->loopStart = startVal;

        this->loopEnd = endVal;

        this->dispFrequency = frequency;

    }

    static unsigned __stdcall ThreadStaticEntryPoint(void * pThis){

        ThreadX* pThX = (ThreadX*) pThis;

        pThX->ThreadEntryPoint();

        return 1;

    }

    void ThreadEntryPoint(){

        for (int i = loopStart; i <= loopEnd; i++){

            cout << threadName << " i = " << i << endl;

            Sleep(100);

        }

    }

};

 

int main(){

    ThreadX * pThX = new ThreadX(0, 10, 2000);

    HANDLE hth1 = NULL;

    unsigned uiThread1ID;

    hth1 =(HANDLE) _beginthreadex(NULL,

        0,

        ThreadX::ThreadStaticEntryPoint,

        pThX,

        CREATE_SUSPENDED,

        &uiThread1ID);

    if (hth1 == NULL){

        cout << "failed to create thread 1" << endl;

       

    }

    DWORD dwExitCode;

    GetExitCodeThread(hth1, &dwExitCode); // shoule be STILL_ACTIVE

    cout << "init thread 1 exit code id = " << dwExitCode << endl;

    pThX->threadName = "yang1";

 

    ThreadX* pThX2 = new ThreadX(0, 10, 2000);

    HANDLE hth2 = NULL;

    unsigned uiThread2ID;

    hth2 = (HANDLE) _beginthreadex(NULL,

        0,

        ThreadX::ThreadStaticEntryPoint,

        pThX2,

        CREATE_SUSPENDED,

        &uiThread2ID);

    if (hth2 == NULL){

        cout << "create thread 2 failed" << endl;

       

    }

    GetExitCodeThread(hth2, &dwExitCode);

    cout << "init thread 2 exit code id = " << dwExitCode << endl;

    pThX2->threadName = "yang2";

 

    ResumeThread(hth1);

    ResumeThread(hth2);

 

    WaitForSingleObject(hth1, INFINITE);

    WaitForSingleObject(hth2, INFINITE);

 

    GetExitCodeThread(hth1, &dwExitCode);

    cout << "thread 1 exited with exit code " << dwExitCode << endl;

    GetExitCodeThread(hth2, &dwExitCode);

    cout << "thread 2 exited with exit code " << dwExitCode << endl;

 

    CloseHandle(hth1);

    CloseHandle(hth2);

 

    delete pThX;

    delete pThX2;

    pThX = NULL;

    pThX2 = NULL;

    cout << "end program  " << endl;

 

    return 0;

}
  • 示例2:
#include
 #include
 #include
 #include
 using namespace std;
 
struct Arg//用来传参给线程函数
{
    double d_;
    string str_;
    Arg(double dd, string ss):d_(dd), str_(ss){}
};
 
//线程绑定的函数返回值和参数是确定的,而且一定要__stdcall
unsigned __stdcall threadFun(void *)
{
    for(int i = 0; i < 10; i++)
        cout<d_<str_<threadfun();
        return 3;
    }
    void threadfun()
    {
        cout<

你可能感兴趣的:(Windows API 多线程 CreateThread、_beginthread、_beginthreadex、AfxBeginThread)