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

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

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

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 循环数组实现线程安全的消息队列

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. 线程同步对象速查表

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

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

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

  • A4988步进驱动

    A4988步进驱动基本知识绕组  常用的步进电机有四根线,1A1B2A2B,1A和1B是一个绕组,2A和2B是一个绕组,用万用表测试1A和1B之间是短路的,2A和2B之间是短路的,1A和1B,2A和2B是等效的。  通常状况下,步进电机可以自由转动(用手可以拧动),1A和1B接在一起的时候,用手拧会感到明显阻力,1A和1B,2A和2B分别接在一起,则阻力更大。步距角  所谓步进电机,就是可以…

  • 设计模式总结

    设计模式总结

  • idea 2021 4 永久激活码(破解版激活)「建议收藏」

    idea 2021 4 永久激活码(破解版激活),https://javaforall.cn/100143.html。详细ieda激活码不妨到全栈程序员必看教程网一起来了解一下吧!

  • php分享朋友圈链接变文字,微信朋友圈怎么转发别人的文章(链接、视频、图片、文字)…「建议收藏」

    php分享朋友圈链接变文字,微信朋友圈怎么转发别人的文章(链接、视频、图片、文字)…「建议收藏」小编每天都有不定时逛微信朋友圈的习惯,嘿嘿,估计正在看此文的你也跟小编一样的吧。朋友圈吸引我们的是上面常能看到很多娱乐、搞笑或有益的帖子。有福同享,独乐不如众乐嘛,SO,偶时也会手痒地转发一下别人发的好文章,与更多人分享。目前微信朋友圈里的链接文章、文字或图片不能一键转发,不过若要转发,也不是难事。如果你还不知道怎么转发别人的文章的话,此文就能帮到你。朋友圈发布的有链接资源文章/视频,图文,或纯文…

  • 外包公司的程序员一般做啥工作?_程序员在外包公司有发展吗

    外包公司的程序员一般做啥工作?_程序员在外包公司有发展吗公众号后台回复“图书“,了解更多号主新书内容作者:年素清来源:码农故事汇01码农故事汇张向阳的学历远达不到百度阿里腾讯等IT大厂的招聘要求,虽然他是根正苗红的计算机科班出身,可学历…

  • ACL 通配符掩码的应用「建议收藏」

    ACL 通配符掩码的应用「建议收藏」ACL(Access Control List) 访问控制列表在作为数据包的过滤器以及在对指定的某种类型的数据包的优先级,起到了对某些数据包的优先级起到了限制流量的作用,减少了网络的拥塞。          通配符掩码作为ACL中重要的一部分,是路由器在进行访问控制时必不可少的重要部件,那么什么是通配符掩码呢?          通配符掩码:路由器使用通配符掩码与原地址或者是目标

发表回复

您的电子邮箱地址不会被公开。

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