[原创][3]探究C#多线程开发细节-“用ConcurrentQueue<T>解决多线程的无顺序性的问题“

[简介]
常用网名: 猪头三
出生日期: 1981.XX.XX
QQ: 643439947
个人网站: 80x86汇编小站 https://www.x86asm.org
编程生涯: 2001年~至今[共22年]
职业生涯: 20年
开发语言: C/C++、80x86ASM、PHP、Perl、Objective-C、Object Pascal、C#、Python
开发工具: Visual Studio、Delphi、XCode、Eclipse、C++ Builder
技能种类: 逆向 驱动 磁盘 文件
研发领域: Windows应用软件安全/Windows系统内核安全/Windows系统磁盘数据安全/macOS应用软件安全
项目经历: 磁盘性能优化/文件系统数据恢复/文件信息采集/敏感文件监测跟踪/网络安全检测

[序言]
经过上一篇文章([原创][2]探究C#多线程开发细节-“线程的无顺序性“-CSDN博客), 得知在不干预的情况下, 默认运行是无顺序的. 那么这样特性, 对程序的运行来说, 是好还是坏呢? 其实无顺序没有好坏之说, 只跟程序功能的业务需求有关系. 当一个业务需求也可以说是功能, 需要多线程的无顺序特性, 那么在写代码的过程中就不要干预它. 如果业务需求对顺序有严格要求, 那么在编写多线程时, 就要适当得干预了.

[到底什么需求和场合需要控制多线程的运行顺序呢?]
这里举例一个最常见的场合: 比如有一个10G的文件, 程序创建了10个线程来读取内容, 每个线程分别依次读取1G内容(0号线程  读取的范围是0~1G, 1号线程 读取的范围1~2G, 2号线程 读取的范围是2~3G, 依次类推), 然后每个线程读取内容完毕之后, 就在程序界面上显示. 

[上面的场合, 如果不控制多线程的运行顺序时, 会发生什么现象呢?]
出现的现象就是: 假设当1号线程最先读取完1~2G范围的内容, 该线程就马上在界面显示. 当显示完成之后, 0号线程才完成读取0~1G范围的内容并在界面显示. 这样内容就错乱了.

这里把刚才抽象的描述实例化: 假设一个文件有10个字, "我爱CSDN编程网站". 每1个线程分别控制1个字的读取和显示, 形成如下关系:

0号线程 读取并显示 "我"
1号线程 读取并显示 "爱"
2号线程 读取并显示 "C"
3号线程 读取并显示 "S"
4号线程 读取并显示 "D"
5号线程 读取并显示 "N"
6号线程 读取并显示 "编"
7号线程 读取并显示 "成"
8号线程 读取并显示 "网"
9号线程 读取并显示 "站"

如果不加以干预的话, 界面上最终显示出来的内容, 有可能是 "编爱CDSN我网站程" 这样错乱的效果.

[那么如何解决这样的问题呢?]
遇到的这样问题, 就需要一个叫ConcurrentQueue<T>类,它是多线程安全的并提供先进先出(FIFO)的数据结构操作方法. 现在看看如何利用ConcurrentQueue<T>类来强制把无顺序的多线程改变成有顺序的.
1> 在使用for循环创建线程的时候, 同时也把线程的编号有序的存放到ConcurrentQueue<T>.
2> 假如当3号线程提前完成了文件内容的读取, 在返回给界面显示之前, 先判断2号线程是否完成, 如果2号线程没有完成, 那么3号线程就原地等待, 直到2号线程把内容读取完毕并在界面显示完成之后, 它才能在界面显示
步骤2是非常关键的逻辑, 这里仅仅是举例3号线程与2号线程的关系, 依次类推, 其他线程也有这样的等待关系.
3> 如何判断2号线程是否完成内容读取与内容显示?
很简单, 直接从ConcurrentQueue<T>的队列头部获取线程编号即可. 如果队列头部的编号为"3",就表示"2"号线程已经完成所有工作任务并轮到3号线程开始干活了.

[下面是一套完整的代码]
这套代码是入门级的, 主要是多线程实验演示. 如果哪个朋友一眼看出, 有更好的代码实现, 说明你是高手了.  如果看不出来, 也不要紧. 后期还有更多的讲解并且释放出更优秀的多线程代码让大家学习.

   public partial class Form_Main : Form{private ConcurrentQueue<int> mpr_cq_ThreadIndex = new ConcurrentQueue<int>();public class Thread_Run{public int mpu_int_ThreadIndex;private Action<int> mpr_action_UpdateWaiteInfo;private ConcurrentQueue<int> mpr_cq_ThreadIndex;public Thread_Run(Action<int> action_param_UpdateWaiteInfo, ref ConcurrentQueue<int> cq_param_ThreadIndex){mpr_action_UpdateWaiteInfo = action_param_UpdateWaiteInfo;mpr_cq_ThreadIndex = cq_param_ThreadIndex;}public int mpu_fun_ShowIndex(){return mpu_int_ThreadIndex;}public void mpu_pro_StartThread(){Thread class_Thread = new Thread(Thread_Exe);class_Thread.Start();}private void Thread_Exe(){int int_Dq_ThreadIndex;// 核心重点: N号线程等待N-1线程是否完成任务while (true){if (mpr_cq_ThreadIndex.TryPeek(out int_Dq_ThreadIndex) && int_Dq_ThreadIndex == mpu_int_ThreadIndex){//调用委托方法来更新UImpr_action_UpdateWaiteInfo?.Invoke(mpu_int_ThreadIndex);mpr_cq_ThreadIndex.TryDequeue(out _);break;}Thread.Sleep(5);}}}// End Thread_Run()public Form_Main(){InitializeComponent();}public void mpu_pro_UpdateWaiteInfo(int int_param_ThreadIndex){if (InvokeRequired){this.Invoke((MethodInvoker)delegate {lb_WaitInfo.Text += (Environment.NewLine + string.Format("{0} 号线程已经跑到终点.", int_param_ThreadIndex));});}}private void Bn_StartThread_Click(object sender, EventArgs e){// 启动10个线程for (int int_Index = 0; int_Index < 10; int_Index++){mpr_cq_ThreadIndex.Enqueue(int_Index);Thread_Run class_ThreadRun = new Thread_Run(mpu_pro_UpdateWaiteInfo, ref mpr_cq_ThreadIndex);class_ThreadRun.mpu_int_ThreadIndex = int_Index;class_ThreadRun.mpu_pro_StartThread();}}}


[总结]
如果你们能按照源码把程序运行起来, 并且理解了本文章的内容, 那么是一个很大的进步. 这做这个代码实验的时候, 如遇到问题, 可在下面留言, 我会一一针对性的回复.


[程序截图]
 

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

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

相关文章

金蝶Apusic应用服务器 任意文件上传漏洞复现

0x01 产品简介 金蝶Apusic应用服务器&#xff08;Apusic Application Server&#xff0c;AAS&#xff09;是一款标准、安全、高效、集成并具丰富功能的企业级应用服务器软件&#xff0c;全面支持JakartaEE8/9的技术规范&#xff0c;提供满足该规范的Web容器、EJB容器以及WebSer…

STM32F407-14.3.7-01PWM输入模式

PWM 输入模式 此模式是输入捕获模式的一个特例。其实现步骤与输入捕获模式基本相同&#xff0c;仅存在以下不同之处&#xff1a; 例如&#xff0c;可通过以下步骤对应用于 TI1① 的 PWM 的周期&#xff08;位于 TIMx_CCR1⑨ 寄存器中&#xff09;和占空 比&#xff08;位于 …

rabbitmq消息队列实验

实验目的&#xff1a;实现异步通信 实验条件&#xff1a; 主机名 IP地址 组件 test1 20.0.0.10 rabbitmq服务 test2 20.0.0.20 rabbitmq服务 test3 20.0.0.30 rabbitmq服务 实验步骤&#xff1a; 1、安装rabbitmq服务 2、erlang进入命令行&#xff0c;查看版本 …

IDEA导入JavaWeb项目(非Maven)

IDEA导入JavaWeb(非Maven)项目教程 运行教程 亲爱的粉丝们&#xff0c;我深知你们对IDEA导入JAVAWeb工程的迫切需求。在这个充满竞争的时代&#xff0c;每一个项目都离不开高效的沟通。过程中需要对应的环境适配和软件…

提升Jmeter测试效率的9种参数化方法!

jmeter工具无论做接口测试还是性能测试&#xff0c;参数化都是一个必须掌握且非常有用的知识点。参数化的使用场景: 1&#xff09;多个请求都是同一个ip地址&#xff0c;若服务器地址更换了&#xff0c;则脚本需要更改每个请求的ip 2&#xff09;注册账号&#xff0c;不允许账…

计算机网络HTTP篇

目录 一、HTTP基本概念 二、GET 与 POST 2.1、GET 与 POST 有什么区别&#xff1f; 2.2、GET 和 POST 方法都是安全和幂等的吗&#xff1f; 三、HTTP 缓存 3.1、强制缓存&#xff1a; 3.2、协商缓存 四、HTTP 特性 4.1、HTTP/1.1 4.1.1、HTTP/1.1 的优点 4.1.2、HTT…

OSError: We couldnt connect to ‘https://huggingface.co‘

最近在做NerF类的数字人口型算法。需要加载一些huggingface上面的模型&#xff0c;但是无法连接上&#xff0c;如下图所示 于是先科学上网&#xff0c;打开https://huggingface.co/models 然后搜索提到的无法加载的模型&#xff0c;比如这里是cpierse/wav2vec2-large-xlsr-53-…

Unity DOTS《群体战斗弹幕游戏》核心技术分析之3D角色动画

最近DOTS发布了正式的版本, 我们来分享现在流行基于群体战斗的弹幕类游戏&#xff0c;实现的核心原理。今天给大家介绍大规模战斗群体3D角色的动画如何来实现。 DOTS 对角色动画支持的局限性 截止到Unity DOTS发布的版本1.0.16,目前还是无法很好的支持3D角色动画。在DOTS 的b…

周报:css相关扩展知识

目录 1. 扩展知识&#xff1a;浮动盒子的排列位置 浮动盒子常见排列特点&#xff1a; 浮动盒子扩展特点&#xff1a; 2.扩展知识:行高的取值 line-height常见取值&#xff1a; 行高的取值的方式&#xff1a; 两个方式的区别&#xff1a; 3.扩展知识&#xff1a;body背景…

鸿蒙系统开发手册 - HarmonyOS内核驱动层源码分析

众所周知系统定义HarmonyOS是一款“面向未来”、面向全场景&#xff08;移动办公、运动健康、社交通信、媒体娱乐等&#xff09;的分布式操作系统。在传统的单设备系统能力的基础上&#xff0c;HarmonyOS提出了基于同一套系统能力、适配多种终端形态的分布式理念&#xff0c;能…

深度学习手势检测与识别算法 - opencv python 计算机竞赛

文章目录 0 前言1 实现效果2 技术原理2.1 手部检测2.1.1 基于肤色空间的手势检测方法2.1.2 基于运动的手势检测方法2.1.3 基于边缘的手势检测方法2.1.4 基于模板的手势检测方法2.1.5 基于机器学习的手势检测方法 3 手部识别3.1 SSD网络3.2 数据集3.3 最终改进的网络结构 4 最后…

Gitee 之初体验(上)

我们在项目开发或者自己学习的时候&#xff0c;总会存在这样的问题&#xff1a; 在一台电脑上编写完代码&#xff0c;想要再另外一台电脑上再去写&#xff0c;再或者和其他人一起协作等等场合&#xff0c;代码传来传去很麻烦。 这个时候&#xff0c;我们就可以去使用代码管理工…