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

用户模式下的线程同步机制提供了非常好的性能,但他们也的确存在一些局限性,而且不适用于许多应用程序,例如,对Interlocked系列函数只能对一个值进行操作,它们从来不会把线程切换到等待状态。我们可以

大家好,又见面了,我是全栈君,今天给大家准备了Idea注册码。

全栈程序员社区此处内容已经被作者隐藏,请输入验证码查看内容
验证码:
请关注本站微信公众号,回复“验证码”,获取验证码。在微信里搜索“全栈程序员社区”或者“www_javaforall_cn”或者微信扫描右侧二维码都可以关注本站微信公众号。

  用户模式下的线程同步机制提供了非常好的性能,但他们也的确存在一些局限性,而且不适用于许多应用程序,例如,对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 循环数组实现线程安全的消息队列

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

#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

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

7. 线程同步对象速查表

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/120148.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 公平锁与非公平锁_公平锁和非公平锁区别

    公平锁与非公平锁_公平锁和非公平锁区别公平锁和非公平锁一、如果一个锁是公平的,那么获取的顺序就应该符合请求的绝对顺序,即FIFO。二、测试结果 非公平性锁可能使线程“饥饿”,为什么它又被设定成默认的实现呢?再次观察上表的结果,如果把每次不同线程获取到锁定义为1次切换,公平性锁在测试中进行了10次切换,而非公平性锁只有5次切换,这说明非公平性锁的开销更小。三、,公平性锁保证了锁的获取按照FIFO原则,而代价是进行…

    2022年8月12日
    6
  • ajax例子详解_mapreduce实例解析

    ajax例子详解_mapreduce实例解析jQueryAjax实例全解析 jQuery确实是一个挺好的轻量级的JS框架,能帮助我们快速的开发JS应用,并在一定程度上改变了我们写JavaScript代码的习惯。废话少说,直接进入正题,我们先来看一些简单的方法,这些方法都是对jQuery.ajax()进行封装以方便我们使用的方法,当然,如果要处理复杂的逻辑,还是需要用到jQuery.ajax()的(这个后面会说到).

    2022年8月16日
    4
  • Docker快速入门总结笔记

    Docker快速入门总结笔记Docker是一个开源的应用容器引擎,基于Go语言并遵从Apache2.0协议开源。Docker可以让开发者打包他们的应用以及依赖包到一个轻量级、可移植的容器中,然后发布到任何流行的Linux机器上,也可以实现虚拟化。…

    2022年6月29日
    26
  • 如何解决虚拟机连不上网「建议收藏」

    如何解决虚拟机连不上网「建议收藏」通常情况下,电脑关机或重启后需要重新连网,但是,虚拟机下的乌班图通常需要重新连网,很多时候找不到之前连接的网络,如果是宽带连接,首先查看虚拟机的设置,将网络适配器改成Net模式(必要时需要重置,然后重启虚拟机),如果还没有出现要连接的以太网,那么就要查看一下主机的服务中的虚拟机是否已经全部开启,如果没有开启,就要将所有和虚拟机有关的服务启动。…

    2022年6月26日
    29
  • meanshift算法图解

    meanshift算法图解本博将对meanshift算法进行总结,包括meanshift算法原理以及公式推导,图解,图像聚类,目标跟踪中的应用及优缺点总结。算法原理meanshift算法其实通过名字就可以看到该算法的核心,mean(均值),shift(偏移),简单的说,也就是有一个点 ,它的周围有很多个点  我们计算点  移动到每个点  所需要的偏移量之和,求平均,就得到平均偏移量,(该偏移量的方向是周围点分布密集…

    2022年7月13日
    12
  • 422模块接线_422接口定义

    422模块接线_422接口定义#硬件芯片422引脚连接方式前言参考连接对应表举例-正好拿手头的一款芯片链接举例查手册前言最近调试串口转422程序,便买了一个“多功能5和1的转换器“,如下图所示,但是调试的时候,没怎么看线序,改线后忘记如何连接,只能重新查找资料,为例方便更多人也方便自己记录参考。参考连接个人觉的写言简意赅的,美中不足的地方就是看着费劲,所以优化了一下。参考如下连接:https://blog.csdn.net/fzktongyong/article/details/86163206对应表举例-正好拿手头的

    2022年8月30日
    0

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号