博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
C++之内核对象进行线程同步
阅读量:5064 次
发布时间:2019-06-12

本文共 5462 字,大约阅读时间需要 18 分钟。

  用户模式下的线程同步机制提供了非常好的性能,但他们也的确存在一些局限性,而且不适用于许多应用程序,例如,对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 
#include
using namespace std;// 数组实现循环消息队列#define ARRAY_SIZE 8template
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
CircleQueue
::CircleQueue():m_nHead(0),m_nTail(0),m_nArraySize(ARRAY_SIZE){ m_hMutex = CreateMutex(NULL, false, NULL); m_pArray = new T[m_nArraySize];}template
CircleQueue
::~CircleQueue(){ delete[] m_pArray; m_pArray = NULL; CloseHandle(m_hMutex);}template
int CircleQueue
::GetLength(){ return (m_nTail-m_nHead+m_nArraySize)%m_nArraySize; }template
bool CircleQueue
::IsEmpty(){ if (m_nTail == m_nHead) { return true; } return false;}template
bool CircleQueue
::IsFull(){ if ((m_nTail + 1) % m_nArraySize == m_nHead) { return true; } return false;}template
T CircleQueue
::PopMsg(){ T msg = static_cast
(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
void CircleQueue
::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 << "消息队列已满,请等待..."<
void CircleQueue
::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
*pQueue1 = new CircleQueue
; CircleQueue
*pQueue2 = new CircleQueue
; 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. 线程同步对象速查表

转载于:https://www.cnblogs.com/xiaobingqianrui/p/8915564.html

你可能感兴趣的文章
Android 常用开源框架源码解析 系列 (四)Glide
查看>>
操作系统概述
查看>>
mysql 根据地图 坐标 查询 周边景区、酒店
查看>>
GIMP永久保存选择的办法
查看>>
<CDQ分治> Jam's problem again [HDU - 5618]
查看>>
mysql重置密码
查看>>
使用request简单爬虫
查看>>
jQuery轮 播的封装
查看>>
一天一道算法题--5.30---递归
查看>>
switchcase的用法
查看>>
React.js 小书 Lesson15 - 实战分析:评论功能(二)
查看>>
CSS3样式
查看>>
为什么不能根据返回类型来区分重载
查看>>
MetInfo安装
查看>>
jQuery选择器 (详解)
查看>>
php扩展安装范例
查看>>
DNA甲基化检测服务
查看>>
Photoshop CS5完全自学实战实例视频教程
查看>>
设计原则
查看>>
桥接模式
查看>>