并发编程实现

一、并行编程

1、Parallel 类

Parallel类是System.Threading.Tasks命名空间中的一个重要类,它提供数据并行和任务并行的高级抽象。

For和ForEach

Parallel类下的For和ForEach对应着普通的循环和遍历(普通的for和foreach),但执行时会尝试在多个线程上同时处理循环迭代。

//For
Parallel.For(0, 10, i =>
{Console.WriteLine($"我是:{i}。我的线程ID是:{Thread.CurrentThread.ManagedThreadId}");
});
//ForEach
List<string> items = new List<string> { "A", "B", "C", "D", "E" };
Parallel.ForEach(items, item =>
{Console.WriteLine($"我是:{item}。我的线程ID是:{Thread.CurrentThread.ManagedThreadId}");
});

与普通For输出后的区别(右为普通For循环),我们可以看到Parallel类下For输出的顺序不是递增的,这是因为它是在不同的线程中执行所导致的。

 

Invoke

尽可能并行执行提供的每个操作,即方法调用。

Action action1 = () => Console.WriteLine("你好");
Action action2 = () => Console.WriteLine("不好");
Parallel.Invoke(action1, action2);

2、PLINQ

PLINQ为Parallel LINQ的缩写,在LINQ中允许你利用多核处理器并行处理数据,PLINQ会自动将查询拆分成多个部分,并在多个线程上并行执行这些部分。

AsParallel()

将顺序的LINQ查询转换为并行的查询,允许利用多核处理器并行处理数据集合,从而加速查询的执行,当调用该方法时,LINQ查询会转换为PLINQ查询。 

int[] data = Enumerable.Range(0, 100).ToArray();
var query =from i in data.AsParallel()where i%10==0select i;

并行度

在默认的情况下,PLINQ会使用计算机上所有的处理器,使用WithDegreeOfParallelism用于指定用于并行查询执行的处理器的最大数量,即同时执行的任务数量。

int[] data = Enumerable.Range(0, 100).ToArray();
var query =from i in data.AsParallel().WithDegreeOfParallelism(3)where i%10==0select i;

排序

默认情况下,PLINQ 不保证输出顺序与输入顺序一致。如果需要保持顺序,可以使用AsOrdered()方法,但是调用该方法时,PLINQ 将尝试在并行处理的同时保留元素的顺序,这也就意味着性能可能会下降。

int[] data = Enumerable.Range(0, 100).ToArray();
var query =from i in data.AsParallel().AsOrdered()where i%10==0select i;

ForAll()

用于并行地遍历查询结果集,并对每个元素执行一个指定的操作,并行执行,可以充分利用多核处理器的性能优势,加快处理速度,但并不保证操作的执行顺序与原始数据集中的顺序一致。

query.ForAll(res =>
{Console.WriteLine(res);
});

二、数据并发控制

1、lock(锁)

lock关键字用于确保当一个线程进入代码的临界区时,其他线程不会进入该临界区,这有助于防止多个线程同时访问同一资源,可能导致数据不一致以及数据被破坏的问题。(lock关键字通常与对象一起使用,该对象用作锁定的目标,通常称为“锁对象”。)

public class Account
{private Object thisLock = new Object();public void Withdraw(){// 这是一个临界区,只有一个线程可以进入  lock (thisLock){try{//模拟工作for (int i = 0; i < 5; i++){Console.WriteLine($"我是{i},我的线程ID:{Thread.CurrentThread.ManagedThreadId}");Thread.Sleep(500);//模拟延迟}}finally{Console.WriteLine($"线程{Thread.CurrentThread.ManagedThreadId}被释放了");// 确保锁被释放,即使发生异常  }}}
}

当我们使用两个或者多个线程同时调用该方法时, 只会有一个线程拿到锁对象进入到临界区,其余的线程会形成阻塞态,一直等待该锁被释放然后进入到临界区(或者任务超时该线程摧毁)。

Account account = new Account();
//创建线程1任务
Task task1 = Task.Run(() => account.Withdraw());
//创建线程2任务
Task task2 = Task.Run(() => account.Withdraw());
//等待两个任务完成
Task.WaitAll(task1, task2);

2、Monitor类

Monitor类允许线程安全地访问共享资源,是System.Threading命名空间的一部分,并且提供了比lock更底层和更灵活的功能,防止多个线程同时访问某个代码段或资源导致数据不一致以及数据被破坏的问题。

public class Account
{private Object thisLock = new Object();public void Withdraw(){//获取锁Monitor.Enter(thisLock);try{for (int i = 0; i < 5; i++){Console.WriteLine($"我是{i},我的线程ID:{Thread.CurrentThread.ManagedThreadId}");Thread.Sleep(500);}}finally{Console.WriteLine($"线程{Thread.CurrentThread.ManagedThreadId}被释放了");//释放锁Monitor.Exit(thisLock);// 确保锁被释放,即使发生异常  }}
}

注意:一个线程获取到了锁,那么其他线程就会形成阻塞态,如果该线程的任务完成之后,没有释放锁,那么后面的线程就一直处于阻塞态的状态,形成死锁。

TryEnter

指定毫秒内获取锁,如果获取到锁则返回true,如果没有获取到锁,则返回false,该方法通常配合一些逻辑执行,任务完成后同样需要Exit来释放锁,否则会造成死锁。

bool MonitorBool = Monitor.TryEnter(thisLock,5000);

3、ReaderWriterLockSlim类

它允许多个读取者同时访问资源,但在写入时则独占资源,即不允许其他读取者或写入者同时访问(读共享写独享)。

1、基本读写锁

读锁(EnterReadLock和ExitReadLock):当多个线程需要同时读取共享资源时,可以使用读锁。多个线程可以同时持有读锁,这意味着它们可以同时读取共享资源,但在此期间,任何线程都不能获得写锁。

写锁(EnterWriteLock和ExitWriteLock):当线程需要修改共享资源时,它必须获得写锁。在写锁被持有的期间,其他线程既不能获得读锁也不能获得写锁,确保共享资源的单独访问。

public class Account
{private ReaderWriterLockSlim rwLock = new ReaderWriterLockSlim();private int sharedData = 0;//写操作public void WriteData(int value){//获取写入锁rwLock.EnterWriteLock();try{//执行写入sharedData = value;Thread.Sleep(2000);//模拟延迟}finally{Console.WriteLine($"{sharedData}写入完成,我的线程ID:{Thread.CurrentThread.ManagedThreadId}");//释放写入锁rwLock.ExitWriteLock();}}//读操作public int ReadData(){//获取读取锁rwLock.EnterReadLock();try{Thread.Sleep(2000);//模拟延迟return sharedData;}finally{Console.WriteLine($"读取完成,我的线程ID:{Thread.CurrentThread.ManagedThreadId}");//释放读取锁rwLock.ExitReadLock();}}
}

2、升级读写锁

升级读锁(EnterUpgradeableReadLock和ExitUpgradeableReadLock):当线程以可升级的方式获取读锁时,它表示该线程可能随后需要升级到写锁,在此期间,其他线程不能获得写锁,但可以获得读锁。

升级写锁(EnterWriteLock):当持有可升级的读锁的线程决定需要修改共享资源时,它可以调用 EnterWriteLock 方法来升级锁,而无需先释放读锁,这将阻塞其他所有尝试获取读锁或写锁的线程,直到写锁被释放。

public class Account
{private ReaderWriterLockSlim rwLock = new ReaderWriterLockSlim();private int sharedData = 0;public void UpgradeableReadAndWriteData(int value){//进入升级读取模式rwLock.EnterUpgradeableReadLock();try{// 执行读操作  int currentData = sharedData;// 假设基于读取的数据,决定需要修改数据  if (currentData != value){// 升级到写锁,而不释放读锁  rwLock.EnterWriteLock();try{// 在写锁下,执行写操作  sharedData = value;Console.WriteLine($"{sharedData}写入,我的线程ID是{Thread.CurrentThread.ManagedThreadId}");}finally{Console.WriteLine($"退出写锁,我的线程ID是{Thread.CurrentThread.ManagedThreadId}");// 退出写锁,但保持读锁  rwLock.ExitWriteLock();}}}finally{Console.WriteLine($"退出升级读锁,我的线程ID是{Thread.CurrentThread.ManagedThreadId}");// 退出可升级的读锁  rwLock.ExitUpgradeableReadLock();}}}

4、Concurrent(并发集合)

BlockingCollection<T>

提供了一种线程安全的方式来在多个生产者线程和消费者线程之间共享数据。当集合为空时,尝试从中取数据的操作会被阻塞,直到有数据可用。同样地,当集合已满(如果设置了容量限制)时,尝试添加数据的操作也会被阻塞。

using System.Collections.Concurrent;BlockingCollection<int> collection = new BlockingCollection<int>();// 启动生产者任务  
Task producerTask = Task.Run(() =>
{for (int i = 0; i < 10; i++){Console.WriteLine("生产者生产数据: " + i);collection.Add(i); // 将数据添加到集合中  Thread.Sleep(1000); // 模拟耗时操作  }// 通知消费者没有更多的数据将要添加  collection.CompleteAdding();
});// 启动消费者任务  
Task consumerTask = Task.Run(() =>
{foreach (var item in collection.GetConsumingEnumerable()){Console.WriteLine("消费者消费数据: " + item);Thread.Sleep(500); // 模拟耗时操作  }
});// 等待生产者和消费者任务完成  
Task.WaitAll(producerTask, consumerTask);

常用类:

ConcurrentBag<T>

这是一个无序的线程安全集合,允许线程安全地添加和移除元素。

ConcurrentDictionary<TKey, TValue>

这是一个线程安全的字典,支持线程安全地添加、移除和访问键值对。

ConcurrentQueue<T>

这是一个线程安全的先进先出(FIFO)集合,支持线程安全地入队和出队操作。

ConcurrentStack<T>

这是一个线程安全的后进先出(LIFO)集合,支持线程安全地推入和弹出操作。

Partitioner<TSource>

用于将数据划分为多个分区,以便并行处理。这通常与TPL(Task Parallel Library)一起使用,以便在多个线程或任务上并行处理数据。

OrderablePartitioner<TSource>

这是一个特殊的分区器,它保留了元素的顺序,允许在并行处理的同时保持元素的顺序。

三、异步流

允许你以异步的方式处理数据流,当你需要在不阻塞线程或其他重要线程的情况下处理大量数据时使用。关键字为IAsyncEnumerable<T>,它允许你以异步的方式枚举数据序列,而不需要一次性加载所有数据到内存中。

public class Account
{public async IAsyncEnumerable<int> GenerateAsyncStream(){for (int i = 0; i < 10; i++){await Task.Delay(1000);yield return i;yield return i + 1;}}
}
IAsyncEnumerable<int> asyncStream = new Account().GenerateAsyncStream();await foreach (int i in asyncStream)
{Console.WriteLine(i);
}

常见的并发编程方法还有异步编程和多线程编程等。 

四、并发的注意

使用并发编程的时候应该避免以下及其其他问题的出现,通常并发编程的错误都是因为多个线程同时访问和修改共享资源,或者由于线程之间的同步和协调不当导致的。

1、竞态条件

竞态条件发生在两个或多个线程同时访问共享资源,并且它们的访问顺序会影响程序的结果,在计算共享数据时,由于多个线程计算顺序的不同,导致最终计算的结果也不同导致的。

2、死锁

当两个或多个线程相互等待对方释放资源时,导致所有线程都无法继续执行,这通常是因为线程间的锁顺序不一致导致的。

3、活锁

当线程们都在忙于响应其他线程的动作,但无法完成它们自己的任务时导致的。

4、饥饿

当其他线程一直占用资源而得不到释放时,某些线程长时间得不到执行的机会而导致的。

5、数据不一致

当多个线程没有正确同步对共享数据的访问时,可能会导致数据的不一致状态,这可能是因为一个线程正在读取数据,而另一个线程同时修改了这些数据。

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

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

相关文章

Python中bisect模块

Python中bisect模块 在Python中&#xff0c;如果我们想维持一个已排序的序列&#xff0c;可以使用内置的bisect模块&#xff0c;例如&#xff1a; import bisect# 用于处理已排序的序列 inter_list [] bisect.insort(inter_list, 3) bisect.insort(inter_list, 2) bisect.in…

python3 Fatal error in launcher: Unable to create process using

python 环境变量 在window系统环境变量 path 中配置 python 的安装目录&#xff0c;目录层级至paython 的安转目录即可。 pip环境变量配置 在path 中增加配置 paython 安装目录下 Scripts 子目录的环境变量。 以上配置完成后&#xff0c;win R 打开命令窗口&#xff0c;输…

汽车商城系统

文章目录 汽车商城系统一、系统演示二、项目介绍三、部分功能截图四、部分代码展示五、底部获取项目源码&#xff08;9.9&#xffe5;带走&#xff09; 汽车商城系统 一、系统演示 汽车商城 二、项目介绍 该汽车商城系统主要分为前台和后台两大功能模块&#xff0c;共包含两种…

利用“AnaTraf“网络流量分析仪轻松诊断和优化网络

网络性能监测和诊断(NPMD)是网络管理和优化的重要环节,准确快速地定位和排除网络故障对于保障业务正常运转至关重要。作为一款专业的网络流量分析设备,AnaTraf网络流量分析仪凭借其强大的流量分析和故障诊断功能,为网络管理者提供了一个高效的网络优化解决方案。 全面掌握网络…

【C++】————类与对象(上)-基础知识

目录 1.面向过程和面向对象初步认识 2.类的引入 3.类的定义 类的两种定义方式&#xff1a; 成员变量命名规则的建议&#xff1a; 4.类的访问限定符及封装 4.1 访问限定符 ​编辑 【面试题】问题&#xff1a;C中struct和class的区别是什么&#xff1f; 4.2 封装 【面试…

ipa 功能包调试,分区算法,覆盖算法测试

参考 wiki 流网络 flow network 解释 相关文章 ipa 分区算法 ipa 分区算法总结&#xff0c;部分算法图解 环境 ubuntu20&#xff0c;ros 版本 noetic 运行测试 按照 readme 提示进行测试&#xff0c;跳过第一个步骤&#xff0c;并不需要 turtlebot3。 执行第三个 launch 报…

【计算机网络篇】数据链路层(10)在物理层扩展以太网

文章目录 &#x1f354;扩展站点与集线器之间的距离&#x1f6f8;扩展共享式以太网的覆盖范围和站点数量 &#x1f354;扩展站点与集线器之间的距离 &#x1f6f8;扩展共享式以太网的覆盖范围和站点数量 以太网集线器一般具有8~32个接口&#xff0c;如果要连接的站点数量超过了…

17_基于Flash和RAM的的文件系统选择

嵌入式系统常见文件系统 本文主要讲述在嵌入式系统中,常见的基于flash和内存(RAM)的文件系统类型,具体选择要结合实际需求灵活选配。 一、基于 Flash 的文件系统 基于 Flash 的文件系统主要包括 JFFS2、 YAFFS、 Cramfs 和 Romfs 等,各种文件系统具有不同的特点,本文将分…

数据结构-二叉树-红黑树

一、红黑树的概念 红黑树是一种二叉搜索树&#xff0c;但在每个节点上增加一个存储位表示节点的颜色&#xff0c;可以是Red或者BLACK&#xff0c;通过对任何一条从根到叶子的路径上各个节点着色方式的限制&#xff0c;红黑树确保没有一条路径会比其他路径长出两倍&#xff0c;…

人工智能生成图像的兴起:区分事实与虚构

人工智能生成图像的兴起&#xff1a;区分事实与虚构 概述 在人工智能 (AI) 已融入我们日常生活的时代&#xff0c;人工智能生成图像的快速发展引发了人们对数字内容真实性的担忧。最近&#xff0c;人工智能生成的图像甚至欺骗了最敏锐的眼睛&#xff0c;这引发了人们对批判性…

饥荒服务器搭建centos

服务器环境需要64位32位不可用 uname -r 查看服务器版本 更新yum sudo yum update 安装依赖环境 sudo yum -y install glibc.i686 libstdc.i686 libcurl4-gnutls-dev.i686 libcurl.i686 screen 安装steam cd /home && mkdir steamcmd && cd steamcmd 国…

动态规划算法练习——计数问题

题目描述 给定两个整数 a 和 b&#xff0c;求 a 和 b 之间的所有数字中 0∼9 的出现次数。 例如&#xff0c;a1024&#xff0c;b1032&#xff0c;则 a 和 b 之间共有 9 个数如下&#xff1a; 1024 1025 1026 1027 1028 1029 1030 1031 1032 其中 0 出现 10 次&#xff0c;1 出现…

创新指南|设计冲刺 – 更快找到成功的创新方案

“ 设计冲刺&#xff08;Design Sprint&#xff09;” 一词与跑步无关&#xff0c;而且不局限于设计&#xff0c;它与引导团队加速创新密切相关。设计冲刺旨在帮助创新团队在很短的时间内解决一个极有价值的问题。本文将深入解析这一法宝&#xff1a;设计冲刺是什么&#xff1f…

会声会影2024中文汉化补丁器附免费激活码序列号

会声会影是一款由加拿大Corel公司发布的视频编辑软件&#xff0c;它以其功能丰富和用户友好的界面而闻名。会声会影2024是该系列的最新版本&#xff0c;它不仅继承了之前版本的强大功能&#xff0c;还引入了一系列新的特性和工具&#xff0c;使得视频编辑更加简单、高效且富有创…

智慧安监中的物联网主机E6000

物联网主机E6000的研发背景主要源于我国对物联网技术在安全生产、环境监测、火灾预警与防控、人员定位与紧急救援等领域的迫切需求。近年来&#xff0c;随着物联网技术的飞速发展&#xff0c;我国政府对智慧安监的重视程度不断提升&#xff0c;相关的政策扶持力度也在加大。在这…

React 第二十七章 Hook useCallback

useCallback 是 React 提供的一个 Hook 函数&#xff0c;用于优化性能。它的作用是返回一个记忆化的函数&#xff0c;当依赖发生变化时&#xff0c;才会重新创建并返回新的函数。 在 React 中&#xff0c;当一个组件重新渲染时&#xff0c;所有的函数都会被重新创建。这可能会…

java基础知识点总结2024版(8万字超详细整理)

java基础知识点总结2024版&#xff08;超详细整理&#xff09; 这里写目录标题 java基础知识点总结2024版&#xff08;超详细整理&#xff09;java语言的特点1.简单性2.面向对象3.分布式4.健壮性5.安全性6.体系结构中立7.可移植性8.解释性9.多线程10.动态性 初识java中的main方…

rust开发web服务器框架,github排名对比

Rocket Star最多的框架 github仓库地址&#xff1a;GitHub - rwf2/Rocket: A web framework for Rust. Rocket 是一个针对 Rust 的异步 Web 框架&#xff0c;重点关注可用性、安全性、可扩展性和速度。 Axum 异步运行时 githuh仓库地址&#xff1a;GitHub - tokio-rs/axum: …

探索数据结构:树与二叉树

✨✨ 欢迎大家来到贝蒂大讲堂✨✨ &#x1f388;&#x1f388;养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; 所属专栏&#xff1a;数据结构与算法 贝蒂的主页&#xff1a;Betty’s blog 1. 树 1.1. 树的定义 树是一种非线性的数据结构&#xff0c;它是由n&a…

第五十二周:文献阅读+STHTNN

目录 摘要 Abstract 文献阅读&#xff1a;用于区域空气质量预测的时空分层传输神经网络 现有问题 提出方法 创新点 方法论 周期特征提取组件(PFEC) 场景动态图模块(SDGM) 时空特征提取组件&#xff08;STEC) 传输注意力模块(TransATT) STHTNN模型 研究实验 数据集…