基于Windows的线程使用

一、内核对象

操作系统创建的资源(resource)有很多种,如进程、线程、文件及信号量、互斥量等。其中大部分都是通过程序员的请求创建的,而且请求方式(请求中使用的函数)各不相同。虽然存在一些差异,但它们之间也有共同点:都是由操作系统创建并管理的资源。


不同资源类型在"管理“方式上也有差异。例如:文件管理中应注册并更新文件相关的数据I/O位置、文件的打开模式等。如果是线程,则应注册并维护线程ID、线程所属进程等信息。操作系统为了以记录相关信息的方式管理各种资源,在其内部生成数据块(可视为结构体变量)。每种资源需要维护的信息不同,所以每种资源拥有的数据块格式也有差异。这类数据块称为”内核对象“。

注意:内核对象的所有者是内核(操作系统),其含义为:内核对象的创建、管理、销毁时机的决定等工作均由操作系统完成。


二、基于windows的线程创建
windows创建线程的API原型如下:

#include <windows.h>
HANDLE CreateThread(
    LPSECURITY_ATTRIBUTES lpThreadAttributes,      // pointer to security attributes
    DWORD dwStackSize,                             // initial thread stack size
    LPTHREAD_START_ROUTINE lpStartAddress,         // pointer to thread function
    LPVOID lpParameter,                            // argument for new thread
    DWORD dwCreationFlags,                         // creation flags
    LPDWORD lpThreadId                             // pointer to receive thread ID
);


参数看起来有些复杂,但只需考虑lpStartAddress和lpParameter这两个参数,剩下的只需传递0或NULL即可。

windows线程的销毁时间点:
windows线程在首次调用的线程main返回时销毁。还有其它方法可以终止线程,但最好的方法就是让线程main函数终止(返回)。


如果线程要调用C/C++标准函数,需要通过如下方法创建线程。因为通过CreateThread函数调用创建出的线程在使用C/C++标准函数时并不稳定。

    #include <process.h>
    uintptr_t _beginthreadex(
        void * security,                     //线程安全相关信息,使用默认设置时传递NULL
        unsigned stack_size,                 //要分配给线程的栈大小,传递0时生成默认大小的栈
        unsigned (* start_address)(void *),  //传递线程的main函数信息
        void * arglist,                      //调用main时传递的参数信息
        unsigned initflag,                   //用于指定线程创建后的行为,传递0时,线程创建后立即进入可执行状态
        unsigned * thrdaddr                  //用于保存线程ID的变量地址值
        );
        //成功返回线程句柄,失败返回0
这个函数和CreateWindow相比,参数个数以及参数的含义和顺序均相同,只是变量名和参数类型有所不同。用上面的函数替换CreateWindow时,只需适当更改数据类型。


_beginthread函数:
这个函数比_beginthreadex更好用,但该函数的问题在于,它会让创建线程时返回的句柄失效,以防止访问内核对象。_beginthreadex就是为了解决这一问题而定义的函数。

    #include <windows.h>
    #include <iostream>
    #include <process.h>
    using namespace std;
    unsigned WINAPI ThreadFunc(void *arg);
    int main(int argc, char *argv[])
    {
        HANDLE hThread;       //线程句柄
        unsigned threadID;    //线程ID
        int param = 5;
        hThread = (HANDLE)_beginthreadex(NULL, 0, ThreadFunc, (void*)¶m, 0, &threadID);
        if (0 == hThread)
        {
            cout << "_beginthreadex() error" << endl;
            return -1;
        }
        Sleep(3000); 
        cout << "end of main" << endl;
        return 0;
    }
    unsigned WINAPI ThreadFunc(void *arg)
    {
        int cnt = *((int*)arg);
        for (int i = 0; i < cnt; i++)
        {
            Sleep(1000); 
            cout << "running thread" << endl;
        }
        return 0;
    }

句柄、内核对象和ID间的关系:
线程也属于操作系统管理的资源,因此会伴随着内核对象的创建,并为了引用内核对象而返回句柄。
可以通过句柄区分内核对象,通过内核对象区分线程。最终,线程句柄成为区分线程的工具。通过_beginthreadex函数的最后一个参数可以获取线程ID。句柄和ID有如下显著特点:
句柄的整数值在不同进程中可能出现重复,但线程ID在跨进程范围内不会出现重复。

线程ID用于区分操作系统创建的所有线程,但通常没有这种需求。

内核对象的2种状态:
资源类型不同,内核对象也含有不同信息。其中,应用程序实现过程中需要特别关注的信息被赋予某种“状态”。例如:线程内核对象中需要重点关注线程是否已终止,所以终止状态又称为“signaled状态”,未终止状态称为“non-signaled状态”。


内核对象的状态及状态查看:
进程和线程的内核对象初始状态是non-signaled状态。内核对象带有一个boolean变量,其初始值为FALSE,此时的状态就是non-signaled状态。如果发生了事件,把该变量改为TRUE,此时的状态就是signaled状态。内核对象类型不同,进入signaled状态的情况也有所区别。


内核对象的状态及状态查看:
进程和线程的内核对象初始状态是non-signaled状态。内核对象带有一个boolean变量,其初始值为FALSE,此时的状态就是non-signaled状态。如果发生了事件,把该变量改为TRUE,此时的状态就是signaled状态。内核对象类型不同,进入signaled状态的情况也有所区别。

系统定义了WaitForSingleObject和WaitForMultipleObjects两个函数。

首先介绍WaitForSingleObject函数,该函数针对单个内核对象验证signaled状态。

DWORD WaitForSingleObject(
      HANDLE hHandle,        // handle to object to wait for
      DWORD dwMilliseconds   // time-out interval in milliseconds
);
//返回值:进入signaled状态返回WAIT_OBJECT_0,超时返回WAIT_TIMEOUT


该函数由于发生事件(变为signaled状态)返回时,有时会把相应内核对象再次更改为non-signaled状态。这种可以再次进入non-signaled状态的内核对象称为“auto-reset模式”的内核对象,而不会自动跳转到non-signaled状态的内核对象称为“manual-reset模式“。下面的函数与上述函数不同,可以验证多个内核对象状态。
    DWORD WaitForMultipleObjects(
      DWORD nCount,             // number of handles in the handle array
      CONST HANDLE *lpHandles,  // pointer to the object-handle array
      BOOL fWaitAll,            // wait flag
      DWORD dwMilliseconds      // time-out interval in milliseconds
    );


修改后的程序:

    #include <windows.h>
    #include <iostream>
    #include <process.h>
    using namespace std;
    unsigned WINAPI ThreadFunc(void *arg);
    int main(int argc, char *argv[])
    {
        HANDLE hThread;       //线程句柄
        DWORD wr;
        unsigned threadID;    //线程ID
        int param = 5;
        hThread = (HANDLE)_beginthreadex(NULL, 0, ThreadFunc, (void*)¶m, 0, &threadID);
        if (0 == hThread)
        {
            cout << "_beginthreadex() error" << endl;
            return -1;
        }
        //传递INFINITE时函数不会返回,直到内核对象变成signaled状态
        if ((wr = WaitForSingleObject(hThread, INFINITE)) == WAIT_FAILED)   
        {
            cout << "thread wait error" << endl;
            return -1;
        }
        cout << "wait result:" << ((wr == WAIT_OBJECT_0) ? "signed" : "time-out");
        cout << endl;
        cout << "end of main" << endl;
        return 0;
    }
    unsigned WINAPI ThreadFunc(void *arg)
    {
        int cnt = *((int*)arg);
        for (int i = 0; i < cnt; i++)
        {
            Sleep(1000);  
            cout << cnt << "running thread" << endl;
        }
        return 0;
    }
    #include <windows.h>
    #include <iostream>
    #include <process.h>
    using namespace std;
    #define NUM_THREAD 50
    unsigned WINAPI threadInc(void *arg);
    unsigned WINAPI threadDes(void *arg);
    long long num = 0;
    int main(int argc, char *argv[])
    {
        HANDLE tHandles[NUM_THREAD];
        cout << "sizeof long long:" << sizeof(long long) << endl;
        for (int i = 0; i < NUM_THREAD; i++)
        {
            if (i % 2)
                tHandles[i] = (HANDLE)_beginthreadex(NULL, 0, threadInc, NULL, 0, NULL);
            else
                tHandles[i] = (HANDLE)_beginthreadex(NULL, 0, threadDes, NULL, 0, NULL);
        }
        WaitForMultipleObjects(NUM_THREAD, tHandles, TRUE, INFINITE);
        cout << "result:" << num << endl;
        return 0;
    }
    unsigned WINAPI threadInc(void *arg)
    {
        for (int i = 0; i < 50000000; i++)
            num -= 1;
        return 0;
    }
    unsigned WINAPI threadDes(void *arg)
    {
        for (int i = 0; i < 50000000; i++)
            num -= 1;
        return 0;
    }



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值