Windows下C++实现多线程之线程同步

转自: http://blog.csdn.net/leonwei/article/details/8956632


上一篇文章windows编程 使用C++实现多线程类仅仅是介绍了怎样用类来实现多线程,这篇文章则重点介绍多线程中数据同步的问题。好了,废话不多说,进入主题。


    问题场景:这里我们假设有这样一个工作流水线(CWorkPipeline),它不断的生成一个SquareInfo的对象,这个对象包含x和y坐标,同时包括一个未得到结果的平方和(squareSum),这些流水线类似于现实世界的工厂不断产出产品(SquareInfo对象),很多个这样的流水线把产生的SquareInfo对象汇集给处理中心。

    处理中心(CProcessCenter)是一个多线程类,它不断接收流水线上的数据,并计算每个SquareInfo对象的squareSum,同时把对应的信息打印出来。


    我们看CWorkPipeline的定义:

  1. /************************************************************************/  
  2. /* FileName: WorkPipeline.h 
  3.  * Date: 2015-5-13 
  4.  * Author: huangtianba 
  5.  * Description: 模拟工作流水线类 
  6.  */  
  7. /************************************************************************/  
  8.   
  9. #pragma once  
  10.   
  11. #include <Windows.h>  
  12. #include <functional>  
  13.   
  14. struct SquareInfo  
  15. {  
  16.     int x;  
  17.     int y;  
  18.     int squareSum;  
  19. };  
  20.   
  21. typedef std::tr1::function<void (const SquareInfo &squareInfo)> FoundItemFn;  
  22.   
  23. class CWorkPipeline  
  24. {  
  25. public:  
  26.     CWorkPipeline();  
  27.     ~CWorkPipeline();  
  28.   
  29.   
  30.     bool Init(const FoundItemFn &foundItemFn);  
  31.     bool UnInit();  
  32.   
  33.     static DWORD CALLBACK WorkThread(LPVOID lpParam);  
  34.     DWORD WorkProc();  
  35.   
  36. private:  
  37.     HANDLE m_hThread;  
  38.     FoundItemFn m_foundItemFn;  
  39. };  
/************************************************************************/
/* FileName: WorkPipeline.h
 * Date: 2015-5-13
 * Author: huangtianba
 * Description: 模拟工作流水线类
 */
/************************************************************************/

#pragma once

#include <Windows.h>
#include <functional>

struct SquareInfo
{
    int x;
    int y;
    int squareSum;
};

typedef std::tr1::function<void (const SquareInfo &squareInfo)> FoundItemFn;

class CWorkPipeline
{
public:
    CWorkPipeline();
    ~CWorkPipeline();


    bool Init(const FoundItemFn &foundItemFn);
    bool UnInit();

    static DWORD CALLBACK WorkThread(LPVOID lpParam);
    DWORD WorkProc();

private:
    HANDLE m_hThread;
    FoundItemFn m_foundItemFn;
};

    这里需要注意的是Init()函数接受一个FoundItemFn的函数对象,这个对象是CProcessCenter出给它的回调,用来接收流水线产生的SquareInfo对象。


    下面是CWorkPipeline类各个函数的实现:

  1. /************************************************************************/  
  2. /* FileName: WorkPipeline.cpp 
  3.  * Date: 2015-5-14 
  4.  * Author: chenzba 
  5.  * Description: 
  6.  */  
  7. /************************************************************************/  
  8.   
  9. #include "stdafx.h"  
  10. #include "WorkPipeline.h"  
  11. #include <time.h>  
  12.   
  13. CWorkPipeline::CWorkPipeline(): m_hThread(NULL)  
  14. {  
  15.   
  16. }  
  17.   
  18.   
  19. CWorkPipeline::~CWorkPipeline()  
  20. {  
  21.   
  22. }  
  23.   
  24.   
  25. bool CWorkPipeline::Init(const FoundItemFn &foundItemFn)  
  26. {  
  27.     srand(unsigned int(time(NULL)));  
  28.   
  29.     // 创建线程  
  30.     m_hThread = CreateThread(NULL, 0, &CWorkPipeline::WorkThread, this, CREATE_SUSPENDED, NULL);  
  31.     if (NULL == m_hThread) {  
  32.         return false;  
  33.     }  
  34.   
  35.     m_foundItemFn = foundItemFn;  
  36.     ResumeThread(m_hThread);  
  37.     return true;  
  38. }  
  39.   
  40.   
  41. bool CWorkPipeline::UnInit()  
  42. {  
  43.     if (m_hThread != NULL)   
  44.     {  
  45.         CloseHandle(m_hThread);  
  46.         m_hThread = NULL;  
  47.     }  
  48.   
  49.     m_foundItemFn = FoundItemFn();  
  50.     return true;  
  51. }  
  52.   
  53.   
  54. DWORD CALLBACK CWorkPipeline::WorkThread(LPVOID lpParam)  
  55. {  
  56.     if (NULL == lpParam) {  
  57.         return 0;  
  58.     }  
  59.   
  60.     CWorkPipeline *lpThis = reinterpret_cast<CWorkPipeline *> (lpParam);  
  61.     return lpThis->WorkProc();  
  62. }  
  63.   
  64.   
  65. // 线程处理函数  
  66. DWORD CWorkPipeline::WorkProc()  
  67. {  
  68.     while (true)  
  69.     {  
  70.         // 声明一个SquareInfo对象,给x和y随机赋值  
  71.         SquareInfo squareInfo = {};  
  72.         squareInfo.x = rand() % 10000;  
  73.         squareInfo.y = rand() % 10000;  
  74.   
  75.         // 将squreInfo通知给回调  
  76.         m_foundItemFn(squareInfo);  
  77.   
  78.         // Sleep一段时间  
  79.         Sleep(max(20, rand() % 2000));  
  80.     }  
  81.           
  82.     return 0;  
  83. }  
/************************************************************************/
/* FileName: WorkPipeline.cpp
 * Date: 2015-5-14
 * Author: chenzba
 * Description:
 */
/************************************************************************/

#include "stdafx.h"
#include "WorkPipeline.h"
#include <time.h>

CWorkPipeline::CWorkPipeline(): m_hThread(NULL)
{

}


CWorkPipeline::~CWorkPipeline()
{

}


bool CWorkPipeline::Init(const FoundItemFn &foundItemFn)
{
    srand(unsigned int(time(NULL)));

    // 创建线程
    m_hThread = CreateThread(NULL, 0, &CWorkPipeline::WorkThread, this, CREATE_SUSPENDED, NULL);
    if (NULL == m_hThread) {
        return false;
    }

    m_foundItemFn = foundItemFn;
    ResumeThread(m_hThread);
    return true;
}


bool CWorkPipeline::UnInit()
{
    if (m_hThread != NULL) 
    {
        CloseHandle(m_hThread);
        m_hThread = NULL;
    }

    m_foundItemFn = FoundItemFn();
    return true;
}


DWORD CALLBACK CWorkPipeline::WorkThread(LPVOID lpParam)
{
    if (NULL == lpParam) {
        return 0;
    }

    CWorkPipeline *lpThis = reinterpret_cast<CWorkPipeline *> (lpParam);
    return lpThis->WorkProc();
}


// 线程处理函数
DWORD CWorkPipeline::WorkProc()
{
    while (true)
    {
        // 声明一个SquareInfo对象,给x和y随机赋值
        SquareInfo squareInfo = {};
        squareInfo.x = rand() % 10000;
        squareInfo.y = rand() % 10000;

        // 将squreInfo通知给回调
        m_foundItemFn(squareInfo);

        // Sleep一段时间
        Sleep(max(20, rand() % 2000));
    }
        
    return 0;
}


    每个流水线对象都有一个线程,线程的工作就是没隔一个不确定的时间产生一个SquareInfo对象,并把它抛给外面(CProcessCenter类对象)。这段代码就是m_foundItemFn(squareInfo)。

    这里值得注意的一点是:UnInit()函数中必须将m_foundItemFn对象置为空对象,假如不这样做,如果CProcessCenter对象已经从栈释放,而流水线线程还没退出,继续调用m_foundItemFn(squareInfo)将导致不可预料的错误。


    下面看看CProcessCenter类的定义:

  1. /************************************************************************/  
  2. /* FileName: ProcessCenter.h 
  3.  * Date: 2015-5-14 
  4.  * Author: chenzba 
  5.  * Description: 数据处理汇集中心,处理多个线程汇集过来的数据 
  6.  */  
  7. /************************************************************************/  
  8.   
  9. #pragma once  
  10. #include <atlbase.h>  
  11. #include <atlsync.h>  
  12. #include <vector>  
  13. #include <list>  
  14.   
  15. struct SquareInfo;  
  16. class CWorkPipeline;  
  17.   
  18. class CProcessCenter  
  19. {  
  20. public:  
  21.     typedef ATL::CComAutoCriticalSection DataLock;  
  22.   
  23. public:  
  24.     CProcessCenter();  
  25.     ~CProcessCenter();  
  26.   
  27.     bool Init();  
  28.     bool UnInit();  
  29.     void DoWork();  
  30.   
  31.     static DWORD CALLBACK ProcessThread(LPVOID lpParam);  
  32.     DWORD ProcessProc();  
  33.   
  34.     //  
  35.     // 被CWorkPileline回调的函数  
  36.     //  
  37.     void NotifyFoundItem(const SquareInfo &squareInfo);  
  38.   
  39.     //  
  40.     // 通知线程退出  
  41.     //  
  42.     void NotifyExit();  
  43.   
  44.     void ProcessData();  
  45. private:  
  46.     std::vector<CWorkPipeline *>    m_workLineList;     // 工作流水线线程列表  
  47.     std::list<SquareInfo>           m_dataList;         // 流水线产生的数据列表  
  48.     DataLock                        m_dataLock;         // m_dataList数据锁  
  49.     HANDLE                          m_hThread;  
  50.     ATL::CEvent                     m_pipeEvent;        // 通知处理数据事件  
  51.     bool                            m_isExit;  
  52. };  
/************************************************************************/
/* FileName: ProcessCenter.h
 * Date: 2015-5-14
 * Author: chenzba
 * Description: 数据处理汇集中心,处理多个线程汇集过来的数据
 */
/************************************************************************/

#pragma once
#include <atlbase.h>
#include <atlsync.h>
#include <vector>
#include <list>

struct SquareInfo;
class CWorkPipeline;

class CProcessCenter
{
public:
    typedef ATL::CComAutoCriticalSection DataLock;

public:
    CProcessCenter();
    ~CProcessCenter();

    bool Init();
    bool UnInit();
    void DoWork();

    static DWORD CALLBACK ProcessThread(LPVOID lpParam);
    DWORD ProcessProc();

    //
    // 被CWorkPileline回调的函数
    //
    void NotifyFoundItem(const SquareInfo &squareInfo);

    //
    // 通知线程退出
    //
    void NotifyExit();

    void ProcessData();
private:
    std::vector<CWorkPipeline *>    m_workLineList;     // 工作流水线线程列表
    std::list<SquareInfo>           m_dataList;         // 流水线产生的数据列表
    DataLock                        m_dataLock;         // m_dataList数据锁
    HANDLE                          m_hThread;
    ATL::CEvent                     m_pipeEvent;        // 通知处理数据事件
    bool                            m_isExit;
};

    首先我们看Init()和UnInit()函数的实现:

  1. /************************************************************************/  
  2. /* FileName: ProcessCenter.cpp 
  3.  * Date: 2015-5-14 
  4.  * Author: chenzba 
  5.  * Description: The implement of CProcessCenter class. 
  6.  */  
  7. /************************************************************************/  
  8.   
  9. #include "stdafx.h"  
  10. #include "ProcessCenter.h"  
  11. #include "WorkPipeline.h"  
  12. #include <functional>  
  13. #include <iostream>  
  14. #include "../boost/tr1/functional.hpp"  
  15.   
  16. CProcessCenter::CProcessCenter(): m_hThread(NULL), m_isExit(false)  
  17. {  
  18.   
  19. }  
  20.   
  21.   
  22. CProcessCenter::~CProcessCenter()  
  23. {  
  24.   
  25. }  
  26.   
  27.   
  28. bool CProcessCenter::Init()  
  29. {  
  30.     // 创建事件  
  31.     BOOL created = m_pipeEvent.Create(NULL, TRUE, FALSE, NULL);  
  32.     if (!created) {  
  33.         return false;  
  34.     }  
  35.   
  36.     m_hThread = CreateThread(NULL, 0, &CProcessCenter::ProcessThread, this, CREATE_SUSPENDED, NULL);  
  37.     if (NULL == m_hThread)  
  38.     {  
  39.         UnInit();  
  40.         return false;  
  41.     }  
  42.   
  43.     // 创建10个工作流水线  
  44.     for (int i = 0; i < 10; i++)  
  45.     {  
  46.         CWorkPipeline *pipeLine = new(std::nothrow) CWorkPipeline();  
  47.         if (pipeLine != NULL)  
  48.         {  
  49.             pipeLine->Init(std::tr1::bind(&CProcessCenter::NotifyFoundItem, this, std::tr1::placeholders::_1));  
  50.             m_workLineList.push_back(pipeLine);  
  51.         }  
  52.     }  
  53.   
  54.     m_isExit = false;  
  55.     ResumeThread(m_hThread);  
  56.      
  57.     return true;  
  58. }  
  59.   
  60.   
  61. bool CProcessCenter::UnInit()  
  62. {  
  63.     // 释放流水线资源  
  64.     std::vector<CWorkPipeline *>::iterator it;  
  65.     for (it = m_workLineList.begin(); it != m_workLineList.end(); it++)  
  66.     {  
  67.         if ((*it) != NULL)  
  68.         {  
  69.             (*it)->UnInit();  
  70.             delete (*it);  
  71.             (*it) = NULL;  
  72.         }  
  73.     }  
  74.     m_workLineList.clear();  
  75.   
  76.     if (m_pipeEvent != NULL) {  
  77.         m_pipeEvent.Set();  
  78.     }  
  79.   
  80.     if (m_hThread != NULL)  
  81.     {  
  82.         WaitForSingleObject(m_hThread, 100);  
  83.         CloseHandle(m_hThread);  
  84.         m_hThread = NULL;  
  85.     }  
  86.     m_pipeEvent.Close();  
/************************************************************************/
/* FileName: ProcessCenter.cpp
 * Date: 2015-5-14
 * Author: chenzba
 * Description: The implement of CProcessCenter class.
 */
/************************************************************************/

#include "stdafx.h"
#include "ProcessCenter.h"
#include "WorkPipeline.h"
#include <functional>
#include <iostream>
#include "../boost/tr1/functional.hpp"

CProcessCenter::CProcessCenter(): m_hThread(NULL), m_isExit(false)
{

}


CProcessCenter::~CProcessCenter()
{

}


bool CProcessCenter::Init()
{
    // 创建事件
    BOOL created = m_pipeEvent.Create(NULL, TRUE, FALSE, NULL);
    if (!created) {
        return false;
    }

    m_hThread = CreateThread(NULL, 0, &CProcessCenter::ProcessThread, this, CREATE_SUSPENDED, NULL);
    if (NULL == m_hThread)
    {
        UnInit();
        return false;
    }

    // 创建10个工作流水线
    for (int i = 0; i < 10; i++)
    {
        CWorkPipeline *pipeLine = new(std::nothrow) CWorkPipeline();
        if (pipeLine != NULL)
        {
            pipeLine->Init(std::tr1::bind(&CProcessCenter::NotifyFoundItem, this, std::tr1::placeholders::_1));
            m_workLineList.push_back(pipeLine);
        }
    }

    m_isExit = false;
    ResumeThread(m_hThread);
   
    return true;
}


bool CProcessCenter::UnInit()
{
    // 释放流水线资源
    std::vector<CWorkPipeline *>::iterator it;
    for (it = m_workLineList.begin(); it != m_workLineList.end(); it++)
    {
        if ((*it) != NULL)
        {
            (*it)->UnInit();
            delete (*it);
            (*it) = NULL;
        }
    }
    m_workLineList.clear();

    if (m_pipeEvent != NULL) {
        m_pipeEvent.Set();
    }

    if (m_hThread != NULL)
    {
        WaitForSingleObject(m_hThread, 100);
        CloseHandle(m_hThread);
        m_hThread = NULL;
    }
    m_pipeEvent.Close();
  1.     m_isExit = true;  
  2.   
  3.     return true;  
  4. }  
    m_isExit = true;

    return true;
}

    这里模拟数据处理中心拥有10个工作流水线,将它们分配在堆上,并用一个vector的指针列表存放。 注意事件对象和线程的创建顺序,ResumeThread要放到最后,这意味着当所有资源创建完成后才启动线程。

    要说明一点,

  1. std::tr1::bind(&CProcessCenter::NotifyFoundItem, this, std::tr1::placeholders::_1)  
std::tr1::bind(&CProcessCenter::NotifyFoundItem, this, std::tr1::placeholders::_1)
    使用bind方法产生一个函数对象,有些人编译的时候可能找不到bind函数。如果编译环境支持c++11(vs2012以上),bind函数属于c++标准库,直接在std::中就能找到;但是在vs2008或者以下,不支持c++11的编译环境(也就是我现在写代码的环境),bind函数是属于boost库的函数。Boost库可以跟C++标准库完美共同工作,并且为其提供扩展功能,因此,这里需要引用Boost库,相关库大家可以到网上了解相关信息。


    下面我们继续看NotifyFoundItem函数的实现:

  1. void CProcessCenter::NotifyFoundItem(const SquareInfo &squareInfo)  
  2. {  
  3.     // 数据同步 加锁  
  4.     CCritSecLock locker(m_dataLock.m_sec);  
  5.     m_dataList.push_back(squareInfo);  
  6.     m_pipeEvent.Set();  
  7. }  
void CProcessCenter::NotifyFoundItem(const SquareInfo &squareInfo)
{
    // 数据同步 加锁
    CCritSecLock locker(m_dataLock.m_sec);
    m_dataList.push_back(squareInfo);
    m_pipeEvent.Set();
}
    我们回到CWorkPipeline中的线程函数,m_foundItemFn(squareInfo),这句代码就进入NotifyFoundItem函数,在这里找个函数被10线程调用,m_dataList是共享数据,这里给m_dataList加锁。下面继续,看看加锁的原因。

  1. DWORD CALLBACK CProcessCenter::ProcessThread(LPVOID lpParam)  
  2. {  
  3.     if (NULL == lpParam) {  
  4.         return 0;  
  5.     }  
  6.   
  7.     CProcessCenter *lpThis = reinterpret_cast<CProcessCenter *> (lpParam);  
  8.     return lpThis->ProcessProc();  
  9. }  
  10.   
  11.   
  12. DWORD CProcessCenter::ProcessProc()  
  13. {  
  14.     while (true)  
  15.     {  
  16.         DWORD dwResult = WaitForSingleObject(m_pipeEvent, 2000);  
  17.         if (dwResult == WAIT_TIMEOUT && (!m_isExit)) {  
  18.             continue;  
  19.         }  
  20.   
  21.         // 处理数据  
  22.         ProcessData();  
  23.   
  24.         if (m_isExit) {  
  25.             break;  
  26.         }  
  27.     }  
  28.   
  29.     return 0;  
  30. }  
DWORD CALLBACK CProcessCenter::ProcessThread(LPVOID lpParam)
{
    if (NULL == lpParam) {
        return 0;
    }

    CProcessCenter *lpThis = reinterpret_cast<CProcessCenter *> (lpParam);
    return lpThis->ProcessProc();
}


DWORD CProcessCenter::ProcessProc()
{
    while (true)
    {
        DWORD dwResult = WaitForSingleObject(m_pipeEvent, 2000);
        if (dwResult == WAIT_TIMEOUT && (!m_isExit)) {
            continue;
        }

        // 处理数据
        ProcessData();

        if (m_isExit) {
            break;
        }
    }

    return 0;
}

  1. void CProcessCenter::ProcessData()  
  2. {  
  3.     std::list<SquareInfo> tempList;  
  4.   
  5.     m_dataLock.Lock();  
  6.     tempList = m_dataList;  
  7.     m_dataList.clear();  
  8.     m_pipeEvent.Reset();  
  9.     m_dataLock.Unlock();  
  10.   
  11.     int counter = 0;  
  12.     std::list<SquareInfo>::iterator it;  
  13.     for (it = tempList.begin(); it != tempList.end(); it++)  
  14.     {  
  15.         counter++;  
  16.         (*it).squareSum = (*it).x * (*it).x + (*it).y * (*it).y;  
  17.         std::cout <<"x: " <<(*it).x <<" y: " <<(*it).y <<" square sum: " <<(*it).squareSum <<std::endl;  
  18.     }  
  19.   
  20.     std::cout <<"-------------------------------------------------------" <<counter <<std::endl;  
  21. }  
void CProcessCenter::ProcessData()
{
    std::list<SquareInfo> tempList;

    m_dataLock.Lock();
    tempList = m_dataList;
    m_dataList.clear();
    m_pipeEvent.Reset();
    m_dataLock.Unlock();

    int counter = 0;
    std::list<SquareInfo>::iterator it;
    for (it = tempList.begin(); it != tempList.end(); it++)
    {
        counter++;
        (*it).squareSum = (*it).x * (*it).x + (*it).y * (*it).y;
        std::cout <<"x: " <<(*it).x <<" y: " <<(*it).y <<" square sum: " <<(*it).squareSum <<std::endl;
    }

    std::cout <<"-------------------------------------------------------" <<counter <<std::endl;
}

  1. void CProcessCenter::NotifyExit()  
  2. {  
  3.     m_isExit = true;  
  4. }  
  5.   
  6.   
  7. void CProcessCenter::DoWork()  
  8. {  
  9.     Sleep(20000);  
  10. }  
void CProcessCenter::NotifyExit()
{
    m_isExit = true;
}


void CProcessCenter::DoWork()
{
    Sleep(20000);
}


    NotifyItemFound函数接收到有数据加入到m_dataList中,就设置m_pipeEvent事件,通知线程处理数据。ProcessProc判断m_isExit为false并且WaitForSingleObject返回不是WAIT_TIMEOUT,进入ProcessData()函数。

    ProcessData声明一个tempList临时对象,然后把m_dataList的值赋给tempList,最后把m_dataList清空。

    首先我们先探讨线程同步问题,假如不给m_dataList加锁,当线程执行到tempList = m_dataList之后,另外一个CWorkPipeline线程获得cpu时间片,执行m_dataList.push(),这时候m_dataList新增一个未处理的数据,接下来返回到tempList = m_dataList后,执行m_dataList.clear(),这样新增的未处理的数据就丢失了,因此这里必须给m_dataList上锁,保证任何时刻只有一个线程获得m_dataList的使用权限。

    这里有人可能会疑问,为什么要另外声明一个tempList对象呢?为什么不直接使用m_dataList用于计算呢?

    原因是,在多线程同步中,我们为了提高程序的执行效率,尽可能的让线程获得共享数据(m_dataList)的使用权限时间最短,假如我们直接使用m_dataList计算数据,那么我们必须对一大堆计算逻辑的代码进行加锁或者在每次访问m_dataList的时候加锁,这样效率是相当慢的。我想大家应该很容易理解明白。


    好了,多线程就暂时讲到这里,语言冗余,请大家多多包涵,也希望大家多多指正其中可能的错误。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值