高并发编程之【多线程】

今天来聊一下线程相关的话题,多线程,网上一搜一大把,但始终没有去总结和自我沉淀,这里起个头just do it,然后一直完善下去吧。

【名词概念】

线程:进程中负责程序执行的执行单元。一个进程中至少有一个线程。(定义为程序的执行路径)

多线程:解决多任务同时执行的需求,合理使用CPU资源。多线程的运行是根据CPU切换完成,如何切换由CPU决定,因此多线程运行具有不确定性。

线程池:基本思想还是一种对象池的思想,开辟一块内存空间,里面存放了众多(未死亡)的线程,池中线程执行调度由池管理器来处理。当有线程任务时,从池中取一个,执行完成后线程对象归池,这样可以避免反复创建线程对象所带来的性能开销,节省了系统的资源。

线程五大状态:新建、就绪、运行、阻塞、死亡

阻塞的原因:1.sleep()   2.I/O阻塞   3.锁  4.等待某个触发条件

线程锁:最常用的是读写锁,就是读写互斥、写写互斥、读读共享。

 

【为什么要使用多线程?】

1.现实异步

2.节省CPU周期的浪费,提高应用程序的效率,提高资源利用率。

 

【如何使用?】

一、System.Threading.Tasks.Task

  • Task.Run(() => do());
  • Task.Factory.StartNew(() => do());

匿名函数形式

二、System.Threading.Tasks.Parallel(提供了For和Foreach系列方法)

实例场景:根据班级Id列表获取各个班级成员的全部信息(含学生姓名和成绩,老师个人信息等),这个执行时间较长所以考虑使用多线程也提高接口效率。代码如下图~

值得参考点
1.不固定创建多少线程数,而是根据所需来灵活创建,实例中是按10取模(若有22个班级,则创建3个线程来同时进行,这可以自己根据业务调整)
2.在线程循环体外申明一个变量,在内部进行写操作锁

 

从上面图中不难看出,出现了一个关键字lock,是因为当多线程出现时,往list中读写是随机并发的所以可能是错乱的,我们称之为“线程不安全”,所以这里加了锁!那么有没有list是线程安全的,无需加锁也能多线程下资源共用呢?答案是有的。

多线程下请使用System.Collections.Concurrent命名空间下的集合类型,ConcurrentDictionary<TKey, TValue> 是并发安全的数据结构,其引入的目的也是为了解决高并发场景下字典数据的共享。

关于ConcurrentDictionary的【坑】

ConcurrentDictionary本质是一个字典,所以是不允许出现重复键的,那么有个方法GetOrAdd,有则获取无则创建,但多线程居然有可能相同key进入此方法体多次!是不是很诡异,说好的线程安全的?!

实战案例和解决方案如下图

图一是线程不安全的,图二是线程安全的。

【问题剖析】
ConcurrentDictionary 确实是线程安全,只不过它的线程安全有一定的限制。
即,并发字典只保证存入字典的值是线程安全的,也就是说虽然执行了两次匿名方法但最终仅有一个值能被放入字典,其它值将会被丢弃。(所以你的方法体内是不在乎线程顺序的)
场景再现:比如多人报名(可能会出现重复报名),GetOrAdd方法体内执行报名函数,那么你就会相同人重复报名;再延申下,如果在缓存场景,这就相当于缓存被穿透了,无法避免高并发时 (为了更新缓存) 对后端数据服务的高频访问。
字典的值设为Lazy,即只有唯一的一个 (延迟加载的) 值会被放入字典,不关心顺序,存在即可。
当然,根据具体业务看是否合适,这里仅分享踩过的坑~

今天就先写到这里吧,后续坚持完善下去。

 

4



微信扫一扫

微信扫一扫

微信扫一扫,分享到朋友圈

高并发编程之【多线程】
嘿!有什么能帮到您的吗?
返回顶部

显示

忘记密码?

显示

显示

获取验证码

Close