多线程程序设计(三)——Guarded Suspension

news/2025/3/14 20:47:53/文章来源:https://www.cnblogs.com/green-cnblogs/p/18764824

本文摘要了《Java多线程设计模式》一书中提及的 Guarded Suspension 模式的适用场景,并针对书中例子(若干名称有微调)给出一份 C++ 参考实现及其 UML 逻辑图,也列出与之相关的模式。

◆ 适用场景

当线程访问的共享数据没有准备好时,让该线程进入等待状态,直到数据被准备好后由其他线程唤醒等待中的线程。

◆ 解决方案

使用 std::condition_variable 对象判断共享数据是否准备好。为了获取数据而等待的线程在等待期间必须解锁互斥元,并在被唤醒后重新锁定互斥元。通过 std::unique_lock 与 std::mutex 的组合来提供此类灵活的操作。

◆ 参考实现

例子模拟了一个客户端线程不断地向请求队列中发送请求,而一个服务器线程不断从请求队列中获取请求的例子。作为共享数据的请求队列(Request_Queue)是能存放和获取请求(Request)的类。如果队列当前没有请求,服务器线程会等待,直到客户端线程发送请求。

class Request
{...stringto_string() const{...}};

Request 是用来表示请求的简单类。

class Client
{...Request_Queue &__queue__;...voidrun()              #1{for (int i = 0; i < 10000; ++i) {shared_ptr<Request> req(new Request(("#" + std::to_string(i))));std::printf("%s requests:\t%s\n", __name__.c_str(), req->to_string().c_str());__queue__.put_request(req);             #2std::this_thread::sleep_for(milliseconds(std::rand()%1000));}}};class Server
{...Request_Queue &__queue__;...voidrun()           #3{for (int i = 0; i < 10000; ++i) {shared_ptr<Request> req = __queue__.get_request();          #4std::printf("%s handles:\t%s\n", __name__.c_str(), req->to_string().c_str());std::this_thread::sleep_for(milliseconds(std::rand()%333));}}};

Client::run() 作为客户端线程的初始函数(#1),向请求队列 Request_Queue 中存放 Request(#2)。Server::run() 作为服务器线程的初始函数(#3),从 Request_Queue 中获取 Request(#4)。

class Request_Queue
{...queue<shared_ptr<Request>>__requests__;mutex__mtx__;condition_variable__cv__;...shared_ptr<Request>get_request()               #1{unique_lock<mutex> lk(__mtx__);     #2__cv__.wait(lk,                             #3[this] {bool empty = __requests__.empty();if (empty)std::printf("No requests in queue. Waiting...\n");return !empty;});shared_ptr<Request> req = __requests__.front();         #4__requests__.pop();return req;}voidput_request(shared_ptr<Request> req)           #5{lock_guard<mutex> lk(__mtx__);__requests__.push(req);__cv__.notify_all();                #6}};

Request_Queue 的 get_request 函数(#1)提供获取请求的接口。使用 unique_lock 锁定互斥元 mutex,紧接着就在 condition_variable 对象上等待用 lambda 函数判断是否有可用的 Request。如果有 Request(empty = false)则 Server 将从 wait() 中退出,然后取出第一个 Request(#4);如果没有 Request(empty = true), Server 将进入等待状态,并解锁互斥元。Request_Queue 的 put_request 函数(#5)提供存放请求的接口。使用 lock_guard 锁定互斥元,放入 Request,然后 Client 唤醒等待中的线程(#6)。

以下类图展现了代码主要逻辑结构,

class

以下顺序图展现了线程并发中的交互。

sequence

◆ 验证测试

笔者在实验环境一中编译代码(-std=c++11)成功后运行可执行文件,

$ g++ -std=c++11 -lpthread guarded_suspension.cpp
$ ./a.out

运行结果如下:

...
Alice requests:	[ Request #2 ]
Bobby handles:	[ Request #2 ]
No requests in queue. Waiting...
Alice requests:	[ Request #3 ]
Bobby handles:	[ Request #3 ]
Alice requests:	[ Request #4 ]
Bobby handles:	[ Request #4 ]
No requests in queue. Waiting...
Alice requests:	[ Request #5 ]
Bobby handles:	[ Request #5 ]
No requests in queue. Waiting...
Alice requests:	[ Request #6 ]
Bobby handles:	[ Request #6 ]
No requests in queue. Waiting...
...

可以看到客户端线程和服务器线程交替地发送或处理请求。

◆ 相关模式

  • 对于如何保护共享数据的思路,要参考 Single Threaded Execution 模式。
  • 如果共享数据没有准备好而要直接退出,就使用 Balking 模式。

◆ 最后

完整的代码请参考 [gitee] cnblogs/18764824 。

致《Java多线程设计模式》的作者结城浩。写作中也参考了《C++并发编程实战》中的若干建议,致作者 Anthony Williams 和译者周全等。

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

西部数据企业级硬盘HC310开盘数据恢复,300G左右数据量耗时半年

这块西数4T企业级硬盘HC310是杭州某研究所送过来的,突发损坏不识别,通电后咯吱咯吱敲盘异响,磁头坏了。这款企业级硬盘目前开盘成功率一般,因为磁头适配很困难,需要反复更换磁头,备件成本很高。这种硬盘的开盘难度跟服务器SCSI或SAS硬盘有的一拼,没有经验甚至拆一个废一…

5分钟,构建国产数据库智能体

近期,圈里很多朋友,都尝试利用 DeepSeek 构建自己的智能体。我也利用腾讯元器,将个人公众号内容做了个智能体,可以实现简单的问答。那么延展来看,智能体除了可利用公众号内容,也可使用离线文件等方式来构建。这不禁让我考虑,是否可用这样方式构造一个数据库智能体。说干…

Ubuntu 22.04 LTS 基于 Docker 部署 WordPress

Ubuntu 22.04 LTS 基于 Docker 部署 WordPress 1. 引言 WordPress 是全球最受欢迎的内容管理系统 (CMS),使用 Docker 可以简化其部署过程。本教程将介绍如何在 Ubuntu 22.04 LTS 上使用 Docker 部署 WordPress。2. WordPress 简介 2.1 WordPress 是什么? WordPress 是全球最流…

7.接雨水

给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。 示例1:输入:height = [0,1,0,2,1,0,1,3,2,1,2,1] 输出:6 解释:上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示…

K近邻算法等

1. KNN算法和KD - tree总结 1.1 KNN算法 模型 K近邻(K - Nearest Neighbors,KNN)算法是一种基本的分类与回归方法。它的模型实际上是对特征空间的划分,给定一个训练数据集,对于新的输入实例,在训练数据集中找到与该实例最邻近的 \(K\) 个实例,然后根据这 \(K\) 个实例的…

正则表达式--java进阶day06

1.正则表达式2.正则表达式的规则、使用3.字符类讲解如图,单独一个a满足正则表达式的规则,所以返回true当删去[]后,正则表达式中的规则就会变为必须是abc,否则不满足条件,即使有一个a该规则是指a-d或者m-p,可以写成[a-dm-p]4.预定义字符类注意事项 正则表达式中存在数量问…

探秘Transformer系列之(13)--- FFN

从零开始解析Transformer,目标是:(1) 解析Transformer如何运作,以及为何如此运作,让新同学可以入门;(2) 力争融入一些比较新的或者有特色的论文或者理念,让老鸟也可以有所收获。探秘Transformer系列之(13)--- FFN 目录探秘Transformer系列之(13)--- FFN0x00 概述0x01…

EXCEL-时间函数

💖简介 在Excel中,时间函数用于处理和操作日期和时间数据; 以下是Excel中常用的时间函数及其常见应用场景的总结.📖函数 ⭐时间函数基础 🌟TIME语法:TIME(hour, minute, second) 功能:将小时、分钟、秒转换为时间序列号(0到0.99999999之间的数值)。 示例:TIME(9,30…

day29linux三剑客----sed

day29linux三剑客----sed单个正则字符还认识组合到一起就晕了,怎么办?本质还是对单个字符没理解.认识*认识.*组合到就一起就蒙了,为什么?还是没想明白.的意义,*的意义正则表达式,从左向右,逐步理解单个字符的意义怎么做? 1.思维脑图写没写? 2.每一个正则表达式的符号,…

3.14 学习记录

基于Android Studio 完成了简单的石家庄地铁购票APP

Android配置

将grade-wrapper.properties中地址改为 https://mirrors.cloud.tencent.com/gradle/gradle-8.11.1-bin.zip 等待下载。一般要几个小时。

sqlserver 的视图创建

首先,什么是视图?视图是一种数据库对象,是从一个或者多个数据表或视图中导出的虚表,视图的结构和数据是对数据表进行查询的结果,只存放视图的定义,不存放视图对应的数据; 其结构和和数据是建立在对表的查询基础上,故表中的数据发生变化,从视图中查询出的数据也随之改变…