C#多线程编程-必知必会

发现问题的能力,运用技术解决问题的能力,是一个技术人成长的关键

f9fef9634bf5b150909384b7bcf59cfb.jpeg

@图片故事:洋姜的花,拍摄于2022年7月23日,地点:北京奥林匹克森林公园 ,摄影师:刘先生

概要:使用C#发起多线程任务十分简单,本文旨在汇总多线程编程的注意事项,重点不在于如何发起多线程,主要内容如下:

  1. 控制线程并发数量

  2. 界定共享资源

  3. 加锁并控制锁范围

  4. 子线程异常处理

  5. 未完成任务取消

    希望对小伙伴儿们有所帮助

01


控制线程并发数量

多线程以多任务并行的方式,加快业务处理速度,但如果线程数量超出了系统的承载能力,反倒会造成系统整体性能下降,如何合理地控制线程并发数量,是多线程开发的关键。

推荐采用信号量机制,可以在线程总数未知的情况下,有效地控制并发线程数量,并且以瀑布流的形式,连续执行后续线程,逻辑清晰可控,执行性能高效。

基础代码逻辑如下:

//semaphoreCount是设定的可并行运行的最大线程数量
//taskCount是需要发起的线程的数量
using (Semaphore semaphore = new Semaphore(semaphoreCount, semaphoreCount))
{var woker = new Worker();Task[] tasks = new Task[taskCount];for (int step = 0; step < taskCount; step++)   {        //获取一个信号量,如果所有信号量都已使用,则等待直到一个被释放        semaphore.WaitOne();        //获得信号量之后,才能发起子线程        tasks[step] = Task.Factory.StartNew((data) => { woker.Work(data); }, innerData).ContinueWith((task) =>                                  {                                      //线程完成,释放信号量                                      semaphore.Release();                                  });    }    //...
}

简单来说,是由于分时操作系统,多任务之间存在线程上下文切换,有兴趣的同学可以尝试一下,一次性启动2000个以上线程,查看计算机的资源耗用情况,以便有更真切的体会。

02


界定共享资源

线程共享资源,一类是业务本身需要多个子线程共同处理的资源,另一类是从性能角度考虑,需要被多个子线程共享的资源。

以数据查询为例,数据库连接是一种昂贵的资源,如果每个子线程单独创建数据库连接,必然会造成浪费,多个线程共用一个数据库连接是更合理的选择,因此,数据库连接便是共享资源。

有兴趣的同学可以测试一下,同时启动50个以上线程,如果每个线程创建一个数据库连接,会造成数据库短时间内无法创建足够连接而报错。

03


加锁并控制锁范围

对共享资源进行访问时,需要加锁保护,防止并发错误。

对于业务本身处理的共享资源,加锁主要是防止数据处理错误;对于集合类型的共享资源,建议首选System.Collections.Concurrent 命名空间下的集合类型,以达到线程安全的目的;对于如数据库连接之类的资源,加锁是为了防止程序异常,如数据库连接、HttpClient对象,在一个请求处理完之前,是不能被其他线程访问的,因此需要加锁,确保串行访问是必须的。

对于锁对象,推荐的写法如下,至于是不是要加static ,要看具体业务场景,静态变量的作用域是整个应用程序,如果有两个以上请求同时到达,那么在访问到加锁代码块时,请求也是串行执行的,普通变量的作用域是当前对象,锁范围也是在当前对象内,请求间相互不影响。

readonly object locker = new object();

04


子线程异常处理

概括成一句话是:在明确异常处理要做什么的情况下,才进行异常处理,否则,让异常抛出,交由外层程序处理即可。参考我上一篇文章:异常处理,究竟是处理什么

多线程下异常处理的不同之处在于:子线程内的异常,不会直接抛出到主线程,而是保存在了Task对象的Exception属性中。因此,需要开发小伙伴判断线程状态,进行异常处理。

基础代码逻辑如下:

Task.Factory.StartNew((data) => { woker.Work(data); }, innerData)            .ContinueWith((task) =>            {                //判断线程处理状态,如果执行失败,则抛出异常                if (task.Status == TaskStatus.Faulted)                {                    throw task.Exception;                }      });

05


未完成任务取消

当某个子线程发生异常之后,取消后续相关线程的执行,符合绝大多数业务逻辑。

取消线程操作需要用到 CancellationTokenSource 类,线程启动时,注册“取消凭证(Token)”,当某个子线程发生异常后,调用CancellationTokenSource的Cancel()方法,通知相关线程取消操作。以后会写一篇CancellationToken的详细介绍。

基础代码逻辑如下:

//声明 CancellationTokenSource
using (CancellationTokenSource cancellation = new CancellationTokenSource())
{Task[] tasks = new Task[taskCount];for (int step = 0; step < steps; step++){semaphore.WaitOne();//注册cancellation.Tokentasks[step] = Task.Factory.StartNew((data) => { woker.Work(data); }, innerData, cancellation.Token).ContinueWith((task) =>{if (task.Status == TaskStatus.Faulted){//通知取消任务cancellation.Cancel(true);throw task.Exception;}semaphore.Release();});}
}

有多线程开发经历的小伙伴,可以看一下自己的代码,是否有对以上几点的处理。以上内容均来自于我个人的经验总结,如有疏漏,欢迎小伙伴补充指正。

最后,说一下对于多线程的认识,了解二次元的小伙伴应该知道一个词:“结界”,线程与结界有很多相似之处,一个子线程就相当于一个结界,结界内外虽处于同一空间,但却属于不同的世界,结界阻断了结界内外的联系,但又可以相互作用,更多相似处,小伙伴们自己体会。


您的反馈是我坚持的动力,欢迎点赞,转发,关注

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

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

相关文章

『中级篇』Dockerfile详解(17)

一般的&#xff0c;Dockerfile 分为四部分&#xff1a;基础镜像信息、维护者信息、镜像操作指令和容器启动时执行指令。 ####官网学习 https://docs.docker.com/engine/reference/builder/#usage ####FROM 必须为第一个命令&#xff0c;指定基础镜像 FROM <image> FROM &…

KestrelServer详解[1]:注册监听终结点(Endpoint)

具有跨平台能力的KestrelServer是最重要的服务器类型。针对KestrelServer的设置均体现在KestrelServerOptions配置选项上&#xff0c;注册的终结点是它承载的最重要的配置选项。这里所谓的终结点&#xff08;Endpoint&#xff09;与“路由”介绍的终结点不是一回事&#xff0c;…

快来加入阿里云大学【云学院】班级助理招募—机会稍纵即逝,错过遥遥无期!...

2019独角兽企业重金招聘Python工程师标准>>> 如果你对云计算、大数据、云安全、人工智能领域感兴趣~ 如果你想从事与此相关的工作~~ 如果你又喜欢边交流边学习的方式~ 那么&#xff0c;加入我们吧&#xff01; 我们将为你提供一个广阔的平台&#xff0c;让你接触到云…

深入理解ajax系列第五篇——进度事件

前面的话 一般地&#xff0c;使用readystatechange事件探测HTTP请求的完成。XHR2规范草案定义了进度事件Progress Events规范&#xff0c;XMLHttpRequest对象在请求的不同阶段触发不同类型的事件&#xff0c;所以它不再需要检査readyState属性。这个草案定义了与客户端服务器通…

对象(poco)深度克隆

提供深度克隆对象功能,基于编译表达式实现&#xff0c;性能与原生代码几无差别&#xff0c;远超 json/binary 序列化实现。1. 简单示例class Person {public int Id { get; set; }public string Name { get; set; }public int Age { get; set; }public DateTime Birth { get; s…

Linux之ACL权限控制

ACL权限控制主要目的是提供传统的owner,group,other的read,wirte,execute权限之外的具体权限设置&#xff0c;可以针对单一用户或组来设置特定的权限 设置ACL权限&#xff1a;setfacl查看ACL权限&#xff1a;getfacl 比如&#xff1a;某一目录权限为 drwx------ 2 root root 40…

WIX、Squarespace、WordPress 三者的优劣分别是什么?

层出不穷的智能建站&#xff0c;模板建站&#xff0c;源码建站&#xff0c;云建站&#xff0c;仿站&#xff0c;各种建站概念都抛洒于红海之中。到底什么样的网站适合自己&#xff0c;什么样的网站值得我们去消费&#xff0c;什么样的网站能长久&#xff0c;是个非常值得思考的…

GitHub 使用

Git 是由 Linux 之父 Linus Tovalds 为了更好的管理 linux 内核开发而创立的分布是版本控制/软件管理配置软件. 简单来说, Git 管理你的 代码的历史记录 的工具. 首先注册账户 (已经完成, moveofgod) 然后, 下载一个 GitHub Desktop(mac), msisgit 客户端 (可以用命令行实现, …

LinkedHashMap 与 HashMap区别

2019独角兽企业重金招聘Python工程师标准>>> LinkedHashMap 与 HashMap区别 &#xff08;非原创&#xff09; HashMap,LinkedHashMap,TreeMap都属于Map Map 主要用于存储键(key)值(value)对&#xff0c;根据键得到值&#xff0c;因此键不允许键重复,但允许值重复。 …

C# 11 中的 file local type

C# 11 中的 file local typeIntro在之前的版本中&#xff0c;我们想要一个类型只在当前的类型中生效&#xff0c;通常我们会在一个类的内部声明一个 private 的类型以此来控制这个类型的访问权限&#xff0c;在 C# 11 中引入了一个 file local type&#xff0c;仅在声明类型的这…

PHP实现类似百度搜索自动完成(代码简单)

一、效果图: 二、HTML代码 <html lang"en"> <head><meta charset"utf-8"><title>jQuery UI 自动完成&#xff08;Autocomplete&#xff09; - 默认功能</title><link rel"stylesheet" href"/public/Auto…

CentOS 搭建Postfix+Dovecot简单邮件系统

2019独角兽企业重金招聘Python工程师标准>>> 服务器信息 系统&#xff1a;CentOS 6.5 minimal版本 主机&#xff1a;虚拟机 虚拟机IP&#xff1a;192.168.128.128/24 宿主IP:10.1.79.24/24 安装postfix 注意&#xff1a;CentOS 7实际上已经用postfixSasl2代替sendma…

php面试题2018

一 、PHP基础部分 1、PHP语言的一大优势是跨平台&#xff0c;什么是跨平台&#xff1f; PHP的运行环境最优搭配为ApacheMySQLPHP&#xff0c;此运行环境可以在不同操作系统&#xff08;例如windows、Linux等&#xff09;上配置&#xff0c;不受操作系统的限制&#xff0c;所以…

学生党的专属定制福利,你想要的这里全都有!

同学们&#xff1a;您好&#xff01;很⾼兴认识⼤家&#xff01;我是微软的 Regional Cloud Advocate Kinfey Lo&#xff0c;感谢您在课余时间打开这封信。踏⼊⾦秋&#xff0c;技术峰会进⼊了旺季&#xff0c;有⾯向商业的&#xff0c;有⾯向开发者的&#xff0c;有⾯向技术社…

Quartus prime16.0 与modelsim ae 联调

前言 quartus和modelsim联调对仿真还是很方便的&#xff0c;当然最好是quartus干综合到烧录的活&#xff0c;modelsim单独仿真。而且ae版的性能比se版差。 流程&#xff1a; 1.配置modelsim ae路径&#xff1a; 我这里是这个路径&#xff0c;根据你自己安装的地方配置路径。 2.…

30分钟搞定后台登录界面(103个后台PSD源文件、素材网站)

去年八月时要做一个OA系统为了后台界面而烦恼&#xff0c;后来写了一篇博客&#xff08;《后台管理UI的选择》&#xff09;介绍了选择过程与常用后台UI&#xff0c;令我想不到的时竟然有许多开发者与我一样都为这个事情而花费不少时间&#xff0c;最后界面效果还是不佳&#xf…

分析拼多多的崛起【产品思维】

最近朋友圈讨论拼多多上市的新闻大火&#xff0c;各有各的看法&#xff0c;很有意思&#xff0c;突然想起前段时间得到上的《梁宁-产品思维30讲》&#xff0c;所以想从数据和产品角度分析分析拼多多的崛起。 一&#xff1a;拼多多的迅速崛起 我们先看看拼多多这几年的成长历程&…

KestrelServer详解[2]: 网络连接是如何创建的?

《KestrelServer详解[1]&#xff1a;注册监听终结点&#xff08;Endpoint&#xff09;》已经详细讲述了如何使用KestrelServer&#xff0c;现在我们来简单聊聊这种服务器的总体设计和实现原理。当KestrelServer启动的时候&#xff0c;注册的每个终结点将转换成对应的“连接监听…

支付宝支付开发流程

支付宝开发流程1、首先我们先谈谈第三方支付所谓第三方支付就是和一些各大银行签约&#xff0c;并具备一定实力和信誉保障的第三方独立机构提供的交易平台目前市面上常见的有支付宝&#xff0c;财付通&#xff0c;网银&#xff0c;易宝支付等&#xff0c;网站需要实现第三方支付…

MQ消息队列之MSMQ

主要参考文章&#xff1a; 消息队列&#xff08;Message Queue&#xff09;简介及其使用 转载于:https://www.cnblogs.com/mailaidedt/p/6599130.html