应无所住,而生其心
排名
4
文章
473
粉丝
3
评论
2
net core webapi post传递参数
庸人 : 确实坑哈,我也是下班好了好几次,发现后台传递对象是可以的,但...
百度编辑器自定义模板
庸人 : 我建议换个编辑器,因为现在百度富文本已经停止维护了,用tinymec...
ICP备案 :渝ICP备18016597号-1
网站信息:2018-2025TNBLOG.NET
技术交流:群号656732739
联系我们:contact@tnblog.net
公网安备:50010702506256
欢迎加群交流技术

.net多线程 , 并行执行Parallel,列表遍历,循环下的并行执行

1761人阅读 2024/9/4 17:33 总访问:5167203 评论:0 收藏:0 手机
分类: .NET

Parallel允许线程并行执行。同时支持最大线程执行数量设置,可以设置最大并发数量。

基础用法

  1. static void Main(string[] args)
  2. {
  3. ParallelOptions parallelOptions = new ParallelOptions();
  4. // 表示最大同时支持三个并行方法,也就是同时开三个线程
  5. parallelOptions.MaxDegreeOfParallelism = 3;
  6. Parallel.Invoke(parallelOptions,
  7. () =>
  8. {
  9. Console.WriteLine("方法1");
  10. },
  11. () =>
  12. {
  13. Console.WriteLine("方法2");
  14. },
  15. () =>
  16. {
  17. Console.WriteLine("方法3");
  18. });
  19. Console.WriteLine("所有并行执行方法结束后执行的");
  20. }

这种用法一般适用于是想要并行执行的方法是比较固定的,比如上面这种固定的三个,比如我们现在遇到的业务场景有个需求就是要请求三个接口,需要请求实验平台、评估平台、考试平台的接口,然后等待三个接口都回来后在根据三个接口返回的数据去综合计算,我们就可以把三个接口请求的代码放到这个并行执行里边,这样三个接口请求的时候不用等待,他们可以一起去执行,不然你需要等待接口1请求回来之后在去请求接口2这样就有点慢了。

循环遍历的时候并行执行

for循环

代码如下:

  1. static void Main(string[] args)
  2. {
  3. ParallelOptions parallelOptions = new ParallelOptions();
  4. parallelOptions.MaxDegreeOfParallelism = 3;
  5. Parallel.For(0, 10, parallelOptions, index =>
  6. {
  7. // 加个时间暂停能看到三个一组出来
  8. Thread.Sleep(1000);
  9. Console.WriteLine($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss,fff")} >>> {index}");
  10. });
  11. Console.WriteLine($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss,fff")} >>> 结束后执行的 ");
  12. Console.ReadLine();
  13. }

注意不能这样写哦

  1. for (int i = 0; i < 10; i++)
  2. {
  3. Parallel.Invoke(() =>
  4. {
  5. Console.WriteLine(i);
  6. });
  7. }

这样写是错的,这样是循环开启了10个Parallel,不会并行执行的,这样输出还是会从0-9顺序输出的,不应该是开启多个Parallel而是一个Parallel里边多个方法。

还要注意一个问题,即时在循环遍历的时候这样去写并行任务也是有问题的

  1. for (int i = 0; i < 10; i++)
  2. {
  3. Task.Run(() =>
  4. {
  5. Console.WriteLine(i);
  6. });
  7. }

它不是并行的从0-9开始输出,而是直接全部输出10:

foreach循环

相比for循环,其实foreach循环用来遍历集合的时候还要方便一点。他里边有很多重构的方法,下面简单的说几个。

用法一:直接使用

  1. static void Main(string[] args)
  2. {
  3. List<string> list = new List<string>()
  4. { "小乔", "大桥", "孙尚香", "貂蝉", "甄宓", "蔡文姬", "杜氏", "邹氏" };
  5. Parallel.ForEach(list, item =>
  6. {
  7. Console.WriteLine($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss,fff")} >>> {item}");
  8. });
  9. Console.WriteLine("所有并行执行方法结束后执行的");
  10. Console.ReadLine();
  11. }

用法二:增加一点配置的

  1. static void Main(string[] args)
  2. {
  3. List<string> list = new List<string>()
  4. { "小乔", "大桥", "孙尚香", "貂蝉", "甄宓", "蔡文姬", "杜氏", "邹氏","甘夫人" };
  5. ParallelOptions parallelOptions = new ParallelOptions();
  6. parallelOptions.MaxDegreeOfParallelism = 3;
  7. // 加上配置的
  8. Parallel.ForEach(list, parallelOptions, item =>
  9. {
  10. // 加个时间暂停能看到三个一组出来
  11. Thread.Sleep(1000);
  12. Console.WriteLine($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss,fff")} >>> {item}");
  13. });
  14. Console.WriteLine("所有并行方法执行结束后执行的");
  15. Console.ReadLine();
  16. }

用法三:foreach也可以直接遍历分组之后的数据

  1. List<LatestClassLesson> latestClassLessons = taskManageService.GetLatestClassLessonNew();
  2. ParallelOptions parallelOptions = new ParallelOptions();
  3. parallelOptions.MaxDegreeOfParallelism = 20;
  4. // 按照班级来分组处理消息发送
  5. Parallel.ForEach(latestClassLessons.GroupBy(a => a.ClassId), parallelOptions, (item) =>
  6. {
  7. // 获取班级id
  8. string classId = item.Key;
  9. // 获取班级下的课程id集合,一个班级对应多个课程
  10. List<string> courseList = item.Select(a => a.CourseId).ToList();
  11. // 下面是一大堆逻辑处理
  12. });

实体也比较简单只有两个字段:

  1. public class LatestClassLesson
  2. {
  3. public string ClassId { get; set; }
  4. public string CourseId { get; set; }
  5. }

用法四:foreach里边也可以用async与await处理异步

  1. // 按照班级来分组处理消息发送
  2. Parallel.ForEach(latestClassLessons.GroupBy(a => a.ClassId), parallelOptions, async (item) =>
  3. {
  4. // 获取班级id
  5. string classId = item.Key;
  6. // 获取班级下的课程id集合,一个班级对应多个课程
  7. List<string> courseList = item.Select(a => a.CourseId).ToList();
  8. // 下面是一大堆逻辑处理,比如这里列两个异步访问接口的方法
  9. ApiReturnData apiResult = await requestTools.GetAsync("/evaluation/api/Notice/GetStuRankReportByClassID?classId=" + classId);
  10. EvalDataDtoWrapp evalDataDtoWrapp = JsonConvert.DeserializeObject<EvalDataDtoWrapp>(apiResult.Data);
  11. ApiReturnData prodResult = await requestTools.GetAsync("/prodedu/api/Report/GetCalssProjectRanking?classid=" + classId);
  12. ProdDataDtoWrapp prodDataDtoWrapp = JsonConvert.DeserializeObject<ProdDataDtoWrapp>(prodResult.Data);
  13. });

.net httpclient 请求可能会报错:One or more errors occurred. (The SSL connection could not be established, see inner exception.)
解决方法参考:https://www.tnblog.net/xiuxin3/article/details/8446

用法五:可以把集合按数字拆分之后在用多线程写入,多线程分批写入,用于需要大量插入数据的情况

比如我要一次性写入1万多条,可能会很慢,影响数据库的性能,还会让请求等待,这个时候就可以把这个待写入的集合根据某个数字拆分成多个子集合,然后开线程来跑这些子集合,
示例代码如下:

  1. // 使用多线程来跑
  2. ParallelOptions parallelOptions = new ParallelOptions();
  3. parallelOptions.MaxDegreeOfParallelism = 9;
  4. // 相当于开9个线程,每个线程跑100条
  5. // 根据100条来拆分这个大的集合
  6. List<List<TaskMessage>> subsets = SplitList(taskMessageList, 100);
  7. // 按照每个线程处理100条,来处理消息发送
  8. Parallel.ForEach(subsets, parallelOptions, (item) =>
  9. {
  10. taskManageService.AddTaskMessageList(item);
  11. });
  12. /// <summary>
  13. /// 根据数字拆分集合
  14. /// </summary>
  15. /// <typeparam name="T"></typeparam>
  16. /// <param name="list"></param>
  17. /// <param name="size"></param>
  18. /// <returns></returns>
  19. public List<List<T>> SplitList<T>(List<T> list, int size)
  20. {
  21. List<List<T>> subsets = new List<List<T>>();
  22. for (int i = 0; i < list.Count; i += size)
  23. {
  24. subsets.Add(list.GetRange(i, size).ToList());
  25. }
  26. return subsets;
  27. }

欢迎加群讨论技术,1群:677373950(满了,可以加,但通过不了),2群:656732739。有需要软件开发,或者学习软件技术的朋友可以和我联系~(Q:815170684)

评价

net core 使用 EF Code First

下面这些内容很老了看这篇:https://www.tnblog.net/aojiancc2/article/details/5365 项目使用多层,把数据库访问...

cAPS.net 保存base64位格式的图片

publicvoidUpload() { //取出图片对应的base64位字符 stringimgBase=Request[&quot;imgBase&quot;]; //c#里边的base6...

Quartz.net实例动态改变周期调度。misfire、Cron

Quartz:Java编写的开源的任务调度作业框架 类似Timer之类定时执行的功能,但是更强大Quartz.NET:是把Quartz转成C# NuGet...

.net Windows服务发布、安装、卸载、监听脚本。服务调试

一、脚本 为方便不用每次都去写安装卸载的脚本1.安装脚本@echooff @echo开始安装【服务】 %SystemRoot%\Microsoft.NET\Fr...

c、VB.net中全角半角转换方法

///&lt;summary&gt; ///转全角的函数(SBCcase) ///&lt;/summary&gt; ///&lt;paramname=&quot;input&quot;&gt;任意字符串...

.net mvc分部页.net core分部页

.net分部页的三种方式第一种:@Html.Partial(&quot;_分部页&quot;)第二种:@{ Html.RenderPartial(&quot;分部页&quot;);}...

C.net 配合小程序实现经过第三方服务器中转文件

某些时候,微信小程序前段上传文件的时候需要经过第三方服务器再将文件上传到客户的服务器;操作如下:1:(小程序内向中端服...

.net实现QQ邮箱发送邮件功能

1、微软已经帮我们封装好了发送邮件的类MailMessage,MailMessage类构造一些邮件信息,然后通过SmtpClient进行邮件发送。Mai...

StackExchange.Redis操作redis(net core支持)

官方git开源地址https://github.com/StackExchange/StackExchange.Redis官方文档在docs里边都是官方的文档通过nuget命令下...

windows 自带的netsh进行端口映射

使用netsh 把本地任意ip的25566端口 映射到192.168.81.234的25565端口netshinterfaceportproxyaddv4tov4listenaddress=0.0....

确保.net程序始终以管理员身份运行

usingSystem; usingSystem.Collections.Generic; usingSystem.Linq; usingSystem.Text; usingSystem.Threading.Tasks; ...

ASP.net Timer细节处理

Timer的用法:1:本人称之为计时器,是asp.net官方的一种。用法即是计时所用 2:关于计时有很多中方式,本人学识有限,暂...

.net core 使用session

tip:net core 2.2后可以直接启用session了,不用在自己添加一次session依赖,本身就添加了使用nuget添加引用Microsoft.AspN...

通俗易懂什么是.net?什么是.net Framework?什么是.net core?

朋友圈@蓝羽 看到一篇文章写的太详细太通俗了,搬过来细细看完,保证你对.NET有个新的认识理解原文地址:https://www.cnblo...

asp.net core2.0 依赖注入 AddTransient与AddScoped的区别

asp.net core主要提供了三种依赖注入的方式其中AddTransient与AddSingleton比较好区别AddTransient瞬时模式:每次都获取一...

asp.net主动推送百度seo

虽然可以使用百度提供的js自动推送,但是估计度娘还是希望主动推送一点。哈哈^_^,女孩子嘛大多都喜欢被动一点。publicclass...