• C++之内核对象进行线程同步


      用户模式下的线程同步机制提供了非常好的性能,但他们也的确存在一些局限性,而且不适用于许多应用程序,例如,对Interlocked系列函数只能对一个值进行操作,它们从来不会把线程切换到等待状态。我们可以用关键段把线程切换到等待状态,但是他们只能用来对同一个进程中的线程进行同步,。此外,在使用关键段的时候我们很容易陷入死锁的情形,因为我们无法为进入关键段指定一个很长等待时间。接下来本文将对使用内核对象进行线程同步的相关知识进行总结。

    1. 等待函数

      等待函数使一个线程自愿进入等待状态,直到指定的内核对象被触发为止。Windows提供了WaitForSingleObject和WaitForMultipleObjects两个等待函数。

    1.1 WaitForSingleObject

      (1)函数格式

        DWORD WaitForSingleObject(

          HANDLE hObject,

          DWORD dwMilliseconds);

        :第二个参数为等待时间,单位毫秒,INFINITE被定义为0xFFFFFFFF  

      (2)示例

    DWORD ret = WaitForSingleObject(hProcess, 5000)
    switch (ret)
    {
    case WAIT_OBJECT_0:
        // Thew process terminated
        break;
    case WAIT_TIMEOUT:
        // The process did not terminated within 5000 milliseconds
        break;
    case WAIT_FAILED:
        // Bad call to function(invalid handle?)
        break;
    }

    1.2 WaitForMultipleObjects

      (1)函数格式

        DWORD WaitForMultipleObjects(
           DWORD dwCount,  // 等待内核对象数量
           CONST HANDLE* phObjects, // 等待内核对象句柄集合
           BOOL bWaitAll,   // 判断是否等待所有内核对象触发
           DWORD dwMilliseconds); // 等待时间

      (2)示例

    HANDLE phObjects[3];
    phObjects[0] = hObject1;
    phObjects[1] = hObject1;
    phObjects[2] = hObject1;
    DWORD ret = WaitForMultipleObjects(sizeof(phObjects)/sizeof(phObjects[0]), phObjects, false, 5000);
    switch (ret)
    {
    case WAIT_TIMEOUT:
        // None of objects became signaled within 5000 milliseconds
        break;
    case WAIT_FAILED:
        // Bad call to function(invalid handle?)
        break;
    case WAIT_OBJECT_0 + 0;
        break;
    case WAIT_OBJECT_0 + 1;
        break;
    case WAIT_OBJECT_0 + 2;
        break;
    }

    2. 事件内核对象

    2.1 成员函数

      (1)CreateEvent

        DWORD CreateEvent(
          PSECURITY_ATTRIBUTES psa,
          BOOL bManualReset, // 手动为TRUE,自动为FALSE
          BOOL bInitialState, // TRUE为触发,FALSE为未触发
          PCTSTR pszName
          );

      (2)OpenEvent

        HANDLE OpenEvent(
          DWORD dwDesiredAccess,
          BOOL bInherit,
          PCTSTR pszName)

      (3)SetEvent

         BOOL SetEvent(Handle hEvent)

      (4)ResetEvent

         BOOL ResetEvent(HANDLE hEvent)

    2.2 重点说明

      (1)事件的触发表示一个操作已经完成,有两种类型的事件对象:手动重置事件和自动重置事件。当一个手动重置事件被触发的时候,正在等待该事件的所有线程都变成可调度状态。而当一个自动重置事件被触发的时候,只有一个正在等待该事件的线程会变成可调度状态。

      (2)其他进程中的线程访问事件对象的方法:CreateEvent、继承、DuplicateHandle或OpenEvent

      (3)对自动重置对象来说,通常不需要调用ResetEvent,这是因为系统会自动将事件重置,相反Microsoft并没有为手动重置对象定义一个等待成功所引起的副作用。

    4. 可等待的计时器内核对象

    5. 信号量内核对象

    6. 互斥量内核对象

      互斥量(mutex)内核对象用来确保一个线程独占对一个资源的访问,互斥量对象包含一个使用计数、线程ID以及一个递归计数。

    6.1 成员函数

      (1)CreateMutex

        HANDLE CreateMutex(
            PSECURITY_ATTRIBUTES psa,
            BOOL bInitialOwner, // FALSE表示不为任何线程占用
            PCTSTR pszName );

      (2)OpenMutex

          HANDLE OpenMutex(
             DWORD dwDesiredAccess,
             BOOL bInHeritHandle,
             PCTSTR pszName);

      (3)ReleaseMutex

          BOOL ReleaseMutex(HANDLE hMutex);

    6.2 循环数组实现线程安全的消息队列

    #include "stdio.h"
    #include <Windows.h>
    #include <iostream>
    using namespace std;
    // 数组实现循环消息队列
    #define ARRAY_SIZE 8
    
    template<typename T>
    class CircleQueue
    {
    public:
        CircleQueue();
        ~CircleQueue();
    
        void PushMsg(T msg);
        T PopMsg();
    
        bool IsFull();
        bool IsEmpty();
        int GetLength();
    
        void PrintCircleQueue();
    
    private:
        T *m_pArray;
        int m_nArraySize;
        int m_nHead;
        int m_nTail;
        HANDLE m_hMutex;
    };
    
    template<typename T>
    CircleQueue<T>::CircleQueue():m_nHead(0),m_nTail(0),m_nArraySize(ARRAY_SIZE)
    {
        m_hMutex = CreateMutex(NULL, false, NULL);
        m_pArray = new T[m_nArraySize];
    }
    
    template<typename T>
    CircleQueue<T>::~CircleQueue()
    {
        delete[] m_pArray;
        m_pArray = NULL;
        CloseHandle(m_hMutex);
    }
    
    template<typename T>
    int CircleQueue<T>::GetLength()
    {
        return (m_nTail-m_nHead+m_nArraySize)%m_nArraySize;    
    }
    
    template<typename T>
    bool CircleQueue<T>::IsEmpty()
    {
        if (m_nTail == m_nHead)
        {
            return true;
        }
        return false;
    }
    
    template<typename T>
    bool CircleQueue<T>::IsFull()
    {
        if ((m_nTail + 1) % m_nArraySize == m_nHead)
        {
            return true;
        }
    
        return false;
    }
    
    template<typename T>
    T CircleQueue<T>::PopMsg()
    {
        T msg = static_cast<T>(NULL);
        if (!IsEmpty())
        {
            DWORD nRet = WaitForSingleObject(m_hMutex, 5000);
            switch (nRet)
            {
            case WAIT_OBJECT_0:
                {
                    msg = m_pArray[m_nHead];
                    cout << "从消息队列取出消息:" << msg << endl;
                    m_nHead = (m_nHead+1) % m_nArraySize;
                }
                break;
            case WAIT_TIMEOUT:
                {
                    cout << "Wait TimeOut!" << endl;
                }
                break;
            case WAIT_FAILED:
                {
                    cout << "Wait FAILED" << endl;
                }
                break;
            }
            ReleaseMutex(m_hMutex);
        }
        else
        {
            cout << "消息队列为空!" << endl;
        }
        return msg;
    }
    
    template<typename T>
    void CircleQueue<T>::PushMsg( T msg )
    {
        if (!IsFull())
        {
            DWORD nRet = WaitForSingleObject(m_hMutex, 5000);
            switch (nRet)
            {
            case WAIT_OBJECT_0:
                {
                    m_pArray[m_nTail] = msg;
                    cout << "添加消息到消息队列:" << msg << endl;
                    m_nTail = (m_nTail+1) % m_nArraySize;
                }
                break;
            case WAIT_TIMEOUT:
                {
                    cout << "Wait TimeOut!" << endl;
                }
                break;
            case WAIT_FAILED:
                {
                    cout << "Wait FAILED" << endl;
                }
                break;
            }
            ReleaseMutex(m_hMutex);
        }
        else
        { 
            cout << "消息队列已满,请等待..."<<endl;
        }
    }
    
    template<typename T>
    void CircleQueue<T>::PrintCircleQueue()
    {
        int nStart = m_nHead;
        int nEnd = m_nTail;
        while(nStart!= nEnd)
        {
            cout << m_pArray[nStart];
            nStart = (nStart+1) % m_nArraySize;
        }
        cout << endl;
    }
    int main()
    {
        CircleQueue<int> *pQueue1 = new CircleQueue<int>;
        CircleQueue<char *> *pQueue2 = new CircleQueue<char *>;
        cout << "消息队列1:" << endl;
        pQueue1->PushMsg(1);
        pQueue1->PushMsg(2);
        pQueue1->PushMsg(3);
        pQueue1->PushMsg(4);
        pQueue1->PopMsg();
        pQueue1->PushMsg(5);
        pQueue1->PushMsg(6);
        pQueue1->PopMsg();
        pQueue1->PushMsg(7);
        pQueue1->PopMsg();
        pQueue1->PushMsg(8);
        pQueue1->PrintCircleQueue();
    
        cout << "消息队列2:" << endl;
        pQueue2->PushMsg("hello");
        pQueue2->PushMsg("world");
        pQueue2->PushMsg("I");
        pQueue2->PushMsg("am");
        pQueue2->PushMsg("coming");
        pQueue2->PopMsg();
        pQueue2->PrintCircleQueue();
        return 0;
    }
    View Code

    7. 线程同步对象速查表

  • 相关阅读:
    三剑客之Grep家族
    Linux默认权限命令:umask
    Linux特殊权限管理SUID|SGID|STICKY
    zabbix的web界面出现乱码解决方案
    启动zabbix-server/agent报错:cannot open "/etc/zabbix/logs/zabbix_server.log": [13] Permission denied
    Zabbix-Agent配置文件详解
    使用yum安装报错:[Errno 256] No more mirrors to try
    grep/sed/awk命令查看指定时间段的日志
    数据库SQL练习(一):数据查询
    连接数据库报错:ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/var/lib/mysql/mysql.sock' (2)
  • 原文地址:https://www.cnblogs.com/xiaobingqianrui/p/8915564.html
Copyright © 2020-2023  润新知