windows C++-使用上下文类实现协作信号量

news2024/9/30 7:32:54

下面演示如何使用 concurrency::Context 类实现协作信号灯类。

Context 类允许你阻止或暂停当前执行上下文。 在当前上下文因资源不可用而无法继续时,阻止或暂停当前上下文很有用。 信号灯是当前执行上下文必须等待资源变为可用的一种情况示例。 信号灯(例如关键节对象)是一个同步对象,支持一个上下文中的代码具有对资源的独占访问权限。 但是,与关键节对象不同,信号灯支持多个上下文同时访问资源。 如果具有信号灯锁的上下文数量达到上限,则每个附加的上下文必须等待另一个上下文释放信号灯锁。

实现信号灯类

声明一个名为 semaphore。 将 public 和 private 节添加到此类。

// A semaphore type that uses cooperative blocking semantics.
class semaphore
{
public:
private:
};

在 semaphore 类的 private 节中,声明一个 std::atomic 变量(用于包含信号灯计数)和一个 concurrency::concurrent_queue 对象(用于包含获取信号灯前必须等待的上下文)。

// The semaphore count.
atomic<long long> _semaphore_count;

// A concurrency-safe queue of contexts that must wait to 
// acquire the semaphore.
concurrent_queue<Context*> _waiting_contexts;

在 semaphore 类的 public 节中,实现构造函数。 构造函数采用一个 long long 值,该值指定可同时具有锁的最大上下文数。

explicit semaphore(long long capacity)
   : _semaphore_count(capacity)
{
}

在 semaphore 类的 public 节中,实现 acquire 方法。 此方法以原子操作的形式递减信号灯计数。 如果信号灯计数变为负数,请将当前上下文添加到等待队列的末尾,并调用 concurrency::Context::Block 方法来阻止当前上下文。

// Acquires access to the semaphore.
void acquire()
{
   // The capacity of the semaphore is exceeded when the semaphore count 
   // falls below zero. When this happens, add the current context to the 
   // back of the wait queue and block the current context.
   if (--_semaphore_count < 0)
   {
      _waiting_contexts.push(Context::CurrentContext());
      Context::Block();
   }
}

在 semaphore 类的 public 节中,实现 release 方法。 此方法以原子操作的形式递增信号灯计数。 如果在递增操作之前信号灯计数为负数,则表示至少有一个上下文正在等待锁。 在这种情况下,请取消阻止等待队列前面的上下文。 

// Releases access to the semaphore.
void release()
{
   // If the semaphore count is negative, unblock the first waiting context.
   if (++_semaphore_count <= 0)
   {
      // A call to acquire might have decremented the counter, but has not
      // yet finished adding the context to the queue. 
      // Create a spin loop that waits for the context to become available.
      Context* waiting = NULL;
      while (!_waiting_contexts.try_pop(waiting))
      {
         Context::Yield();
      }

      // Unblock the context.
      waiting->Unblock();
   }
}
示例

此示例中的 semaphore 类的行为是协作的,因为 Context::Block 和 Context::Yield 方法会暂停执行,使运行时可执行其他任务。

acquire 方法会递减计数器,但在另一个上下文调用 release 方法之前,它可能不会完成将上下文添加到等待队列的操作。 为了解释这一点,release 方法使用一个旋转循环来调用 concurrency::Context::Yield 方法,等待 acquire 方法完成添加上下文的操作。

在 acquire 方法调用 Context::Block 方法之前,release 方法可以调用 Context::Unblock 方法。 无需防止此争用条件,因为运行时允许按任意顺序调用这些方法。 如果在 acquire 方法为同一上下文调用 Context::Block 之前 release 方法调用了 Context::Unblock,那么该上下文保持在不受阻止的状态。 运行时仅要求对 Context::Block 的每次调用与对 Context::Unblock 的相应调用匹配。

以下示例显示了完整的 semaphore 类。 wmain 函数显示此类的基本用法。 wmain 函数使用 concurrency::parallel_for 算法创建多个需要访问信号灯的任务。 由于 3 个线程可随时保留锁,因此一些任务必须等待另一个任务完成并释放锁后才能执行。

// cooperative-semaphore.cpp
// compile with: /EHsc
#include <atomic>
#include <concrt.h>
#include <ppl.h>
#include <concurrent_queue.h>
#include <iostream>
#include <sstream>

using namespace concurrency;
using namespace std;

// A semaphore type that uses cooperative blocking semantics.
class semaphore
{
public:
   explicit semaphore(long long capacity)
      : _semaphore_count(capacity)
   {
   }

   // Acquires access to the semaphore.
   void acquire()
   {
      // The capacity of the semaphore is exceeded when the semaphore count 
      // falls below zero. When this happens, add the current context to the 
      // back of the wait queue and block the current context.
      if (--_semaphore_count < 0)
      {
         _waiting_contexts.push(Context::CurrentContext());
         Context::Block();
      }
   }

   // Releases access to the semaphore.
   void release()
   {
      // If the semaphore count is negative, unblock the first waiting context.
      if (++_semaphore_count <= 0)
      {
         // A call to acquire might have decremented the counter, but has not
         // yet finished adding the context to the queue. 
         // Create a spin loop that waits for the context to become available.
         Context* waiting = NULL;
         while (!_waiting_contexts.try_pop(waiting))
         {
            Context::Yield();
         }

         // Unblock the context.
         waiting->Unblock();
      }
   }

private:
   // The semaphore count.
   atomic<long long> _semaphore_count;

   // A concurrency-safe queue of contexts that must wait to 
   // acquire the semaphore.
   concurrent_queue<Context*> _waiting_contexts;
};

int wmain()
{
   // Create a semaphore that allows at most three threads to 
   // hold the lock.
   semaphore s(3);

   parallel_for(0, 10, [&](int i) {
      // Acquire the lock.
      s.acquire();

      // Print a message to the console.
      wstringstream ss;
      ss << L"In loop iteration " << i << L"..." << endl;
      wcout << ss.str();

      // Simulate work by waiting for two seconds.
      wait(2000);

      // Release the lock.
      s.release();
   });
}

此示例生成以下示例输出。

In loop iteration 5...
In loop iteration 0...
In loop iteration 6...
In loop iteration 1...
In loop iteration 2...
In loop iteration 7...
In loop iteration 3...
In loop iteration 8...
In loop iteration 9...
In loop iteration 4...
编译代码

复制示例代码,并将它粘贴到 Visual Studio 项目中,或粘贴到名为 cooperative-semaphore.cpp 的文件中,再在 Visual Studio 命令提示符窗口中运行以下命令。

cl.exe /EHsc cooperative-semaphore.cpp
可靠编程

可使用“资源获取即初始化”(RAII) 模式将对 semaphore 对象的访问限制到给定范围。 在 RAII 模式下,数据结构在堆栈上分配。 该数据结构在创建资源时初始化或获取资源,并在销毁数据结构时销毁或释放该资源。 RAII 模式保证在封闭范围退出之前调用析构函数。 因此,当引发异常或函数包含多个 return 语句时,可以正确管理资源。

以下示例定义一个名为 scoped_lock 的类,该类在 semaphore 类的 public 节中定义。 scoped_lock 类与 concurrency::critical_section::scoped_lock 和 concurrency::reader_writer_lock::scoped_lock 类相似。 semaphore::scoped_lock 类的构造函数获取对给定的 semaphore 对象的访问权限,析构函数释放对该对象的访问权限。

// An exception-safe RAII wrapper for the semaphore class.
class scoped_lock
{
public:
   // Acquires access to the semaphore.
   scoped_lock(semaphore& s)
      : _s(s)
   {
      _s.acquire();
   }
   // Releases access to the semaphore.
   ~scoped_lock()
   {
      _s.release();
   }

private:
   semaphore& _s;
};

以下示例修改了传递至 parallel_for 算法的工作函数的主体,以便它使用 RAII 来确保在函数返回之前释放信号灯。 此技术可确保工作函数是异常安全的。

parallel_for(0, 10, [&](int i) {
   // Create an exception-safe scoped_lock object that holds the lock 
   // for the duration of the current scope.
   semaphore::scoped_lock auto_lock(s);

   // Print a message to the console.
   wstringstream ss;
   ss << L"In loop iteration " << i << L"..." << endl;
   wcout << ss.str();

   // Simulate work by waiting for two seconds.
   wait(2000);
});

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2179059.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

《程序猿之Redis缓存实战 · Redis 与数据库一致性》

&#x1f4e2; 大家好&#xff0c;我是 【战神刘玉栋】&#xff0c;有10多年的研发经验&#xff0c;致力于前后端技术栈的知识沉淀和传播。 &#x1f497; &#x1f33b; CSDN入驻不久&#xff0c;希望大家多多支持&#xff0c;后续会继续提升文章质量&#xff0c;绝不滥竽充数…

多模态人像编辑:PortraitGen将2D肖像视频提升到4D 高斯场

这篇文章《Portrait Video Editing Empowered by Multimodal Generative Priors》&#xff0c;作者是来自中国科学技术大学。文章介绍了一种名为PortraitGen的肖像视频编辑方法&#xff0c;它使用多模态生成先验来实现一致性和富有表现力的风格化编辑。 文章地址&#xff1a;P…

electron教程(三)窗口设置

在main.js文件中&#xff0c;创建窗口时会设置窗口的大小&#xff0c;其实还有很多其他属性&#xff0c;可以根据实际需求选择设置&#xff0c;但部分属性存在局限性&#xff0c;官网也有明确告知&#xff1a;自定义窗口 | Electron (electronjs.org) 项目文件目录如下&#x…

6.1 微服务 服务发现 架构模式分类 应用实践

微服务 服务发现 架构模式分类 应用实践 目录概述需求&#xff1a; 设计思路实现思路分析1.类型-客户端发现2.类型-服务端服务发现3.工具-Eureka4.工具-Consul5.工具-zookper服务发现的挑战服务发现的最佳实践 参考资料和推荐阅读 Survive by day and develop by night. talk …

【三步 完全离线搭建 openwebui 】

完全离线linux 版open webui 的搭建 1.在具有网络连接的环境中下载whl 在有网络的环境&#xff0c;使用pip download可以保存所有的依赖包,可以使用-i 指定清华的镜像源加速下载速度。 # 命令&#xff1a; pip download <package_name> --only-binary:all: --wheel --…

CANoe_DBC能够打开但是无法使用“BusType”

解决DBC文件在CAPL中调用问题&#xff1a;从CANdb到CAPL的顺畅过渡 在汽车电子和嵌入式系统开发中&#xff0c;DBC&#xff08;Database CAN&#xff09;文件作为描述CAN&#xff08;Controller Area Network&#xff09;通信协议的重要工具&#xff0c;广泛应用于网络设计、测…

前端考核总结

目录 JavaScript的基本数据类型有哪些&#xff1f;JavaScript中数据类型的检测方法JavaScript如何判断对象中的属性存在自身还是原型链上flex布局HTML5新标签Vue的基本概念Vue生命周期JavaScript中闭包的基本概念防抖节流双等号与三等号的区别显式转换 JavaScript的基本数据类型…

Flume实战--Flume中的选择器、自动容灾(故障转移)、负载均衡的详解与操作

本文详细介绍了Apache Flume的关键特性&#xff0c;包括选择器、拦截器、故障转移和负载均衡。选择器负责将数据分发到多个Channel&#xff0c;拦截器用于修改或丢弃Event。故障转移机制能够在Sink故障时自动切换&#xff0c;而负载均衡则在多个Sink间分配负载。文章还提供了自…

【零基础入门产品经理】学习准备篇 | 需要学一些什么呢?

前言&#xff1a; 零实习转行产品经理经验分享01-学习准备篇_哔哩哔哩_bilibili 该篇内容主要是对bilibili这个视频的观后笔记~谢谢美丽滴up主友情分享。 全文摘要&#xff1a;如何在0实习且没有任何产品相关经验下&#xff0c;如何上岸产品经理~ 目录 一、想清楚为什么…

Redis 基础数据改造

优质博文&#xff1a;IT-BLOG-CN 一、服务背景 基础数据查询服务&#xff1a;提供航司、机场、票台、城市等基础数据信息。 痛点一&#xff1a;因为基础数据不属于频繁更新的数据&#xff0c;所以每个应用都有自己和缓存&#xff0c;当基础数据更新后&#xff0c;各个应用缓存…

webGL入门(五)绘制多边形

代码&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</title><scri…

ARM 服务器上安装 OpenEuler (欧拉)

系统介绍 在 2019 年 7 月 19 日&#xff0c;华为宣布要在年底正式开源 openEuler 操作系统&#xff1b;在半年后的 12 月 31 日&#xff0c;华为正式开源了 openEuler 操作系统&#xff0c;邀请社区开发者共同来贡献。 一年后&#xff0c;截止到 2020 年12 月 25日&#xff…

计算机毕业设计 Java教务管理系统的设计与实现 Java实战项目 附源码+文档+视频讲解

博主介绍&#xff1a;✌从事软件开发10年之余&#xff0c;专注于Java技术领域、Python人工智能及数据挖掘、小程序项目开发和Android项目开发等。CSDN、掘金、华为云、InfoQ、阿里云等平台优质作者✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精…

[Cocoa]_[初级]_[使用NSNotificationCenter作为目标观察者实现时需要注意的事项]

场景 在开发Cocoa程序时&#xff0c;由于界面是用Objective-C写的。无法使用C的目标观察者[1]类。如果是使用第二种方案2[2],那么也需要增加一个代理类。那么有没有更省事的办法&#xff1f; 说明 开发界面的时候&#xff0c;经常是需要在子界面里传递数据给主界面&#xff0…

PIKACHU | PIKACHU 靶场 XSS 后台配置

关注这个靶场的其他相关笔记&#xff1a;PIKACHU —— 靶场笔记合集-CSDN博客 PIKACHU 自带了一个 XSS 平台&#xff0c;可以辅助我们完成 XSS 攻击&#xff0c;但是该后台需要配置数据库以后才能使用。本教程&#xff0c;就是教大家如何配置 PIKACHU XSS 平台的。 PIKACHU XS…

vulhub weblogic 靶场攻略

一&#xff1a;WebLogic 后台弱⼝令GetShell&#xff08;weak_password &#xff09; 漏洞描述 通过弱⼝令进⼊后台界⾯ , 上传部署war包 , getshell 影响范围 全版本&#xff08;前提后台存在弱⼝令&#xff09; 环境搭建 cd vulhub-master/weblogic/weak_password doc…

【STM32开发环境搭建】-4-在STM32CubeMX中新增Keil(MDK-ARM) 5的工程目录(包含指定路径的C和H文件)

案例背景&#xff1a; 由于Keil(MDK-ARM)5工程&#xff1a;DEMO_STM32F030C8T6.uvprojx是由STM32CubeMX工具生成的&#xff0c;如果我们在Keil工程中手动添加了一些c文件和h文件的Include Path包含路径&#xff0c;会在STM32CubeMX下一次生成uvprojx文件时&#xff0c;被删除&…

纯软件小白 学习DDR5

问题 1.你知道当你打开游戏加载存档时候计算机是在做什么吗&#xff1f; 由于你的CPU只有在数据被加载到DRAM的时候才可以工作&#xff0c;所以当你需要用数据的时候&#xff0c;数据会从SSD复制到DRAM这一过程需要时间&#xff0c;所以会有加载&#xff08;所有3D模型、纹理…

【从零开始实现stm32无刷电机FOC】【实践】【7.1/7 硬件设计】

目录 stm32电路磁编码器电路电机驱动电路电流采样电路电机选择本文示例硬件说明 为了承载和验证本文的FOC代码工程&#xff0c;本节设计了一个简易的三相无刷电机 硬件套件&#xff0c;主控采用非常常用的stm32f103c8t6单片机&#xff0c;电机编码器采用MT6701&#xff0c;电机…

电源的带载能力怎么判断?Namisoft为您介绍测试方法

确保电源在各种负载条件下都能稳定工作&#xff0c;是电源设计者面临的重要挑战。本文将详细介绍如何通过带载测试来评估电源的负载能力。 电源带载测试介绍 带载能力指电源在其规定条件下&#xff0c;所能承受的最大负载能力。电源带载测试就是对电源模块的负载能力进行测试&a…