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

news2024/12/26 21:48:43

[简介]
常用网名: 猪头三
出生日期: 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)
                   {
                       //调用委托方法来更新UI
                       mpr_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.coloradmin.cn/o/1273678.html

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

相关文章

数据中心布线解决方案比较: DAC 电缆和 AOC 光缆

在当今的数字时代&#xff0c;数据中心是无数行业的支柱&#xff0c;它确保了信息的交换并维护关键数据的完整性。为了保持这些数据中心高效运行&#xff0c;选择正确的布线解决方案至关重要。在这方面&#xff0c;两种流行的选择是直连铜缆 (DAC) 和有源光缆 (AOC)。在本文中&…

论文绘图——局部细节放大

文章目录 前言一、绘图1.0版二、绘图2.0版三、总结 前言 我们经常在论文中会看到下面这些样式的图片&#xff1a;在图片中使用矩形框框出感兴趣的区域&#xff0c;然后在底部或者其他位置附上对应的局部放大的细节图&#xff0c;简洁好看。✨ 曾尝试使用过PPT制作&#xff0c;也…

用于缓存一些固定名称的小组件

项目中&#xff0c;用于缓存姓名、地名、单位名称等一些较固定名称的id-name小组件。用于减少一些表的关连操作和冗余字段。优化代码结构。扩展也方便&#xff0c;写不同的枚举就行了。 具体用法&#xff1a; {NameCacheUser.USER.getName(userId);NameCacheUser.ACCOUNT.getN…

GAN:WGAN前作

WGAN前作&#xff1a;有原则的方法来训练GANs 论文&#xff1a;https://arxiv.org/abs/1701.04862 发表&#xff1a;ICLR 2017 本文是wgan三部曲的第一部。文中并没有引入新的算法&#xff0c;而是标是朝着完全理解生成对抗网络的训练动态过程迈进理论性的一步。 文中基本是…

Django 模板引擎 (四)

一、Django模板引擎 一个强大的工具&#xff0c;用于在HTML页面中嵌入动态内容。它使用一种被称为Django模板语言&#xff08;Django Template Language&#xff09;的简单而强大的语法来处理模板。该模板语言使用”{% %}”进行标记&#xff0c;用于执行各种操作。 二、Django…

高效率:使用DBeaver连接spark-sql

提高运行效率一般采取底层使用spark引擎替换成hive引擎的方式提高效率&#xff0c;但替换引擎配置较为复杂考虑到兼容版本且容易出错&#xff0c;所以本篇将介绍使用DBeaver直接连接spark-sql快速操作hive数据库。 在spark目录下运行以下命令&#xff0c;创建一个SparkThirdSe…

TA、TB、TC油封各自用途

在机械系统应用中&#xff0c;油封在防止润滑剂从机器和轴承间隙泄漏方面发挥着至关重要的作用。在各种类型的油封中&#xff0c;常用的是TA、TB、TC密封件。本文将深入探讨这三种密封件各自的用途。 TA、TB和TC密封件都是油封的类型&#xff0c;但它们的设计和应用有所不同。…

java设计模式学习之【对象池模式】

文章目录 引言对象池模式简介定义与用途实现方式 使用场景优势与劣势对象池模式在Spring中的应用JDBC对象池示例代码地址小结 引言 对象池模式在资源管理和性能优化方面发挥着重要作用。这种模式通过重复使用已经初始化的对象&#xff0c;而不是频繁创建和销毁&#xff0c;减少…

计算机体系结构----流水线技术(三)

本文仅供学习&#xff0c;不作任何商业用途&#xff0c;严禁转载。绝大部分资料来自----计算机系统结构教程(第二版)张晨曦等 计算机体系结构----流水线技术&#xff08;三&#xff09; 3.1 流水线的基本概念3.1.1 什么是流水线3.1.2 流水线的分类1. 部件级流水线、处理机级流…

图解系列--HTTPS,认证

确保 Web 安全的HTTPS 1.HTTP 的缺点 1.1.通信使用明文可能会被窃听 加密处理防止被窃听 加密的对象可以有这么几个。 (1).通信的加密 HTTP 协议中没有加密机制&#xff0c;但可以通过和 SSL&#xff08;Secure Socket Layer&#xff0c;安全套接层&#xff09;或TLS&#xff…

搭建JMeter分布式压测环境轻松应对

引言 您想要提高您的应用程序的性能吗&#xff1f;想要确保它在高负载下仍然能够正常工作吗&#xff1f;那么&#xff0c;您一定需要一个可靠的性能测试工具来帮助您完成这个任务。 JMeter是一个广泛使用的性能测试工具&#xff0c;但是如果您的测试需求变得更加复杂和庞大&a…

数据结构与算法编程题35

用按层次顺序遍历二叉树的方法&#xff0c;统计树中具有度为1的结点数目。 #define _CRT_SECURE_NO_WARNINGS#include <iostream> using namespace std;typedef char ElemType; #define ERROR 0 #define OK 1 #define Maxsize 100 #define STR_SIZE 1024typedef struct B…

Google Chrome访问出现 NET::ERR_CERT_INVALID

Google Chrome访问出现 NET::ERR_CERT_INVALID然后访问不了当前网站&#xff0c;这个是由于证书失效了&#xff0c;临时解决方式是&#xff1a; 第一种方案&#xff1a; 在Chrome提示“您的连接不是私密连接”页面的空白区域点击一下&#xff0c;然后输入“thisisunsafe”(页…

[CustomMessages] section

[CustomMessages] section用来定义自定义的一些{cm:}常量. 一个定义和使用的例子。 [CustomMessages] CreateDesktopIconCreate a &desktop icon[Tasks] Name: desktopicon; Description: "{cm:CreateDesktopIcon}"CustomMessages是支持带参数的&#xff0c;从…

「Verilog学习笔记」状态机-重叠序列检测

专栏前言 本专栏的内容主要是记录本人学习Verilog过程中的一些知识点&#xff0c;刷题网站用的是牛客网 读入数据移位寄存&#xff0c;寄存后的数据与序列数做对比&#xff0c;相等则flag为1&#xff0c;不等则为0 timescale 1ns/1nsmodule sequence_test2(input wire clk ,in…

Ascend C 算子开发遇到的问题及解决方法

摘要&#xff1a;在学习Ascend C算子开发进阶课程的时候&#xff0c;进行Ascend C自定义算子工程、算子调用等实验&#xff0c;在开发环境中遇到了一些问题&#xff0c;在这里记录一下。 首先如果在启智社区CANN版本为6.3 &#xff0c;要进行Ascend C算子开发&#xff0c;需要…

TA-Lib学习研究笔记——Volume Indicators (四)

TA-Lib学习研究笔记——Volume Indicators &#xff08;四&#xff09; 1.AD Chaikin A/D Line 量价指标 函数名&#xff1a;AD 名称&#xff1a;Chaikin A/D Line 累积/派发线&#xff08;Accumulation/Distribution Line&#xff09; 简介&#xff1a;Marc Chaikin提出的一…

C++ 学习之匿名名字空间的使用细节

匿名命名空间&#xff08;anonymous namespace&#xff09;是C中的一种特殊命名空间&#xff0c;它没有显式的名称。匿名命名空间可以用来定义仅在当前文件中可见的全局变量、函数和类。 由于没有名字&#xff0c;所以相当于直接引入&#xff0c;但是没有引入定义 如果发生冲…

Influx集群解决方案(Influx Proxy篇)

InFluxDB 集群搭建 本次搭建使用influx proxy 介绍 github地址:https://github.com/chengshiwen/influx-proxy/ Influx Proxy 是一个基于高可用、一致性哈希的 InfluxDB 集群代理服务&#xff0c;实现了 InfluxDB 高可用集群的部署方案&#xff0c; 具有动态扩/缩容、故障恢复…

JRT和检验共用的打印层实现

之前对接的打印和导出是C#实现的&#xff0c;如果要完全Java化就需要用Java把打印元素绘制协议用Java实现&#xff0c;这次介绍实现主体搭建&#xff0c;最终使JRT达到完全信创和跨平台目标。到这篇后&#xff0c;所有的Java难题都解决完毕&#xff0c;几天到几周之内就可以把打…