• windows编程 使用C++实现多线程类


    有时候我们想在一个类中实现多线程,主线程在某些时刻获得数据,可以“通知”子线程去处理,然后把结果返回。下面的实例是主线程每隔2s产生10个随机数,将这10随机数传给多线程类,让它接收到数据后马上打印出来。

    首先看类的定义:

    [cpp] view plain copy
     
    1. #pragma once  
    2. #include <iostream>  
    3. #include <atlbase.h>    // 使用到了atl类  
    4. #include <atlsync.h>  
    5. #include <vector>  
    6. using namespace std;  
    7.   
    8. class CMultiThreadTest  
    9. {  
    10. public:  
    11.     bool Init();    // 初始化类成员  
    12.     bool UnInit();  // 释放资源  
    13.     void NotifyDowork(const std::vector<int> &data);  
    14.   
    15.     static DWORD CALLBACK TestThread(LPVOID);   // 线程函数,必须是静态函数  
    16.     DWORD TestProc();                           // 线程工作实现  
    17.   
    18. private:  
    19.     std::vector<int> m_data;    // 同步数据  
    20.     ATL::CEvent m_NotifyEvent;  // 通知事件  
    21.     HANDLE m_hThread;           // 线程句柄  
    22. };  


    类中使用到了ALT类,需要包含atlbase.h和altsync.h头文件。函数TestThread必须是静态函数,因为CreateThread只接受全局或者静态函数。
    首先先看Init()和UnInit()函数的实现:

    [cpp] view plain copy
     
    1. bool CMultiThreadTest::Init()  
    2. {  
    3.     // 创建事件  
    4.     BOOL bRet = m_NotifyEvent.Create(NULL, TRUE, FALSE, NULL);  
    5.     if (!bRet) {  
    6.         return false;  
    7.     }  
    8.   
    9.     // 挂起的方式创建线程  
    10.     m_hThread = CreateThread(NULL, 0, &CMultiThreadTest::TestThread, this, CREATE_SUSPENDED, NULL);  
    11.     if (NULL == m_hThread) {  
    12.         return false;  
    13.     }  
    14.   
    15.     // 唤醒线程  
    16.     ResumeThread(m_hThread);  
    17.     return true;  
    18. }  
    19.   
    20.   
    21. bool CMultiThreadTest::UnInit()  
    22. {  
    23.     // 通知线程处理data的数据  
    24.     if (m_NotifyEvent != NULL) {  
    25.         m_NotifyEvent.Set();  
    26.     }  
    27.   
    28.     if (m_hThread != NULL)   
    29.     {  
    30.         // 预留100ms让线程处理完数据,100ms是个估值  
    31.         WaitForSingleObject(m_hThread, 100);  
    32.         CloseHandle(m_hThread);  
    33.         m_hThread = NULL;  
    34.     }  
    35.   
    36.     return true;  
    37. }  


    ATL::CEvent的成员函数Create接收4个参数,第四个参数指定Event的名字(它是可以有名字的),以便在其他进程可以找到该事件,这里我们不需要使用,把它设置为NULL,其他参数很容易理解,不赘述。
    Init()函数值得注意的是我们创建的线程是以挂起的方式创建,所以必须调用ResumeThread唤醒线程,否则线程一值处于沉睡状态,不执行线程函数。
    UnInit()函数比较简单,主要通知线程执行收尾工作,并释放类的资源。

    下面我们来看看剩下的函数的实现。

    [cpp] view plain copy
     
    1. DWORD CALLBACK CMultiThreadTest::TestThread(LPVOID lpParam)  
    2. {  
    3.     if (lpParam == NULL) {  
    4.         return 0;  
    5.     }  
    6.   
    7.     CMultiThreadTest *lpThis = reinterpret_cast<CMultiThreadTest *>(lpParam);  
    8.     return lpThis->TestProc();  
    9. }  
    10.   
    11.   
    12. DWORD CMultiThreadTest::TestProc()  
    13. {  
    14.     while (true)  
    15.     {  
    16.         // 每5s监听一次,秒数直接影响程序的性能  
    17.         DWORD dwRet = WaitForSingleObject(m_NotifyEvent, 5000);  
    18.   
    19.         // 进入循环5s没有事件发生,不做任何处理  
    20.         if (dwRet == WAIT_TIMEOUT) {  
    21.             continue;  
    22.         }  
    23.   
    24.     // 打印数组  
    25.         for (unsigned int i = 0; i < m_data.size(); i++)  
    26.         {  
    27.             cout <<m_data[i] <<" ";  
    28.         }  
    29.   
    30.         cout <<endl;  
    31.   
    32.         // 重置事件  
    33.         m_NotifyEvent.Reset();  
    34.     }  
    35.     return 0;  
    36. }  
    37.   
    38.   
    39. void CMultiThreadTest::NotifyDowork(const std::vector<int> &data)  
    40. {  
    41.     m_data = data;  
    42.     m_NotifyEvent.Set();    // 通知线程该做事情了!  
    43. }  


    首先我们看TestThread函数,它是线程的“入口“,线程被唤醒后执行该函数。值得注意的是,我们在创建线程的时候把对象指针this作为参数传递给创建线程函数,系统在调用TestThread的时候会把this传递回来,这里使用弱类型转换reinterpret_cast将LPVOID转化为CMultiThreadTest类的指针,reinterpret_cast是一个危险的类型转换,一般只适用于指针和整数之间的转换。有兴趣的同学可以参考C++ Primer第4版18.2.1章节。


    线程函数将参数lpParam转化为对象指针后,执行对象的成员函数TestProc(),TestProc()实现主要的逻辑。这里可能会有人疑问,为什么不直接在TestThread()函数实现主要逻辑呢?这样做有两个好处,一是能够将线程函数逻辑和业务逻辑分离,其二就是TestThread是个静态函数,类静态函数只能处理类的静态成员变量,而很多时候我们希望线程处理类的非静态成员变量。
    最后NotifyDowork函数很简单,该函数给外部调用,它把外部传进来的data赋值给类的非静态成员变量m_data,并通知线程处理m_data数据,TestProc中WaitForSingleObject函数接收到事件后往下执行,把m_data打印出来。

    下面我们看看main函数的实现:

    [cpp] view plain copy
     
    1. int _tmain(int argc, _TCHAR* argv[])  
    2. {  
    3.     CMultiThreadTest multiThreadTest;  
    4.       
    5.     // 初始化失败  
    6.     if (!multiThreadTest.Init()) {  
    7.         return 0;  
    8.     }  
    9.   
    10.     srand(unsigned int(time(NULL)));  
    11.     std::vector<int> data;  
    12.   
    13.     while (true)  
    14.     {  
    15.         data.clear();  
    16.   
    17.         // 产生10个随机数  
    18.         for (int i = 0; i < 10; i++)  
    19.             data.push_back(rand() % 1000);  
    20.   
    21.         // 通知多线程类执行工作  
    22.         multiThreadTest.NotifyDowork(data);  
    23.   
    24.         Sleep(2000);  
    25.     }  
    26.   
    27.     multiThreadTest.UnInit();  
    28.     return 0;  
    29. }  


    这段代码就不用解释了,记得包含头文件windows.h、time.h和vector。

    总结:
    多线程类的使用场景是,当一个线程或得到数据后,希望其他线程能够处理这部分数。多线程类实现还是比较简单的,首先创建线程和线程事件,实现给外部调用的接口,外部通过接口设置事件,通知线程执行。

     

  • 相关阅读:
    HTML5
    js实现查找字符串中最多的字符的个数
    get和post的区别
    第十七篇 类的特殊成员
    第十八篇 面向对象修饰符
    MariaDB+Keepalived双主高可用配置MySQL-HA
    linux命令详解——crontab
    Java的内存泄漏
    jvm监控工具jconsole进行远程监控配置
    loadrunner执行场景时报Error -27040: Data Format Extension: Init: Internal error问题解决
  • 原文地址:https://www.cnblogs.com/daochong/p/6908694.html
Copyright © 2020-2023  润新知