C# 已经提供了我们几种非常好用的类库如 BackgroundWorker、Thread、Task等,借助它们,我们就能够分分钟编写出一个多线程的应用程序。

  比如这样一个需求:有一个 Winform 窗体,点击按钮后,会将窗体中的数据导出到一个 output.pdf 文件中。原先的代码没有采用多线程技术,所以当点击按钮后,整个窗体就变成无响应了。为了解决这个问题,可以使用 Task.Run(()=>{...导出文件的代码});

  上面的代码看似简单,却隐藏着种种危机。如果在导出的期间,窗体的数据被修改了,那会怎么样?如果多个窗体同时导出到同一个文件,又会怎么样?

  在看完本系列后,你就会清楚了。

  有点了解的朋友都知道线程同步有多种手段,什么 mutex、moniter、seamphore、event 等等,我把它们归为三类,对应三种需要线程同步的情景。

情景一:此茅坑有主了

  当一个资源同时被多个线程访问时,有可能会造成资源冲突(尤其是在存在多个写线程的时候)的情景。遇到这种情况,在 C# 中,我们可以使用 Interlocked、lock、Moniter、SpinLock、ReadWriteLockSlim、Mutex 来处理问题。

关于不同方案间的区别,请猛击这里

  什么情况下会被认为是情景一?

  当你设计的类中出现静态变量、IO操作时,就会遇到情景一。因为这些资源是由多个对象共享的,不同的线程很同时去访问这些资源时,就可能会出现争用。

  当一个类被设计成单例,且包含实例变量时,也会遇到情景一。因为实例变量属于这个单例,当多个线程操纵此单例时,该变量可能会被争用。

  当一个类中的方法调用线程操作某个实例变量时,也会遇到情景一。

情景二:数量有限,先到先得

  情景一强调的是一对多的情形,而在情景二中,资源的数量并不唯一。相比于情景一,情景二侧重的是数量上的限制。而用于实现这一需求的类有:Semaphore、SemaphoreSlim。

  关于不同方案间的区别,请猛击这里

  什么情况下会被认为是情景二?

  当所操作的公共资源存在并发数限制的时候(如数据库连接、IIS连接数限制等),就被认为是情景二。

情景三:我让你动,你才能动!

  情景三关注的是线程执行过程中的先后顺序,而用于保证这种先后顺序的方式就是通过线程通信的方式:ManualResetEventSlim、ManualResetEvent、AutoResetEvent。

  关于不同方案间的区别,请猛击这里

  什么情况下会被认为是情景三?

  当两个线程所处理的事情有先后的依赖时,比如线程二的执行过程依赖线程一的执行结果,那就认为是情景三。

不限使用情景

  上面的各种方案并不是绝对只限于某一场景,比如 AutoResetEvent 即可以用于情景三,也可以用于情景一。但是,杀鸡焉用牛刀,虽然使用 AutoResetEvent 能够实现情景一的需求,但是用不了 AutoResetEvent 的线程通信能力,同时又会有一些额外的限制(每个线程必须保证 wait 和 set 的成对使用,否则一个线程在锁定资源后就可能被另一个线程解锁)。

    lock (m)
{
//....
} //等价于如下方式
autoResetEvent.WaitOne();
//....
autoResetEvent.Set();

  也有朋友说,可以用情景一中的 lock 方案来实现情景三的需求。

    AutoResetEvent autoReset = new AutoResetEvent(false);
private void button1_Click(object sender, EventArgs e)
{
Task.Run(() =>
{
autoReset.WaitOne();
Console.WriteLine("步骤二");
}); Thread.Sleep();//故意延迟从而保证第二个线程是在第一个线程之后才执行
Task.Run(() =>
{
Console.WriteLine("步骤一");
autoReset.Set();
});
}

  上面这个例子最终输出的结果可想而知。此实例说明,不管线程实际的执行顺序如何,AutoResetEvent 都能很容易的保证两个线程的执行顺序。

  如果用 lock 呢?

    private void button1_Click(object sender, EventArgs e)
{
Task.Run(() =>
{
lock (s)
{
Console.WriteLine("步骤一");
}
}); Thread.Sleep();//必须人为确保步骤二的线程要在步骤一的线程之后执行
Task.Run(() =>
{
lock (s)
{
Console.WriteLine("步骤二");
}
});
}

  虽然能实现,但是需要花费额外的代码去人为保证两个线程的执行顺序。

  如何在这么多方案中确定最终所使用的,需要你能对项目的各种情景进行分析,根据实际情景选择对应的方案,而不至于大材小用。

总 结

  通过本系列文章的介绍,希望让大家能对多线程中可能碰到的情景有一个概念,不至于在面临多线程的时候手忙脚乱。

  本文来自《C# 基础回顾: 线程同步的三类情景

C# 线程同步的三类情景的更多相关文章

  1. [7] Windows内核情景分析---线程同步

    基于同步对象的等待.唤醒机制: 一个线程可以等待一个对象或多个对象而进入等待状态(也叫睡眠状态),另一个线程可以触发那个等待对象,唤醒在那个对象上等待的所有线程. 一个线程可以等待一个对象或多个对象, ...

  2. [No000017D]改善C#程序的建议6:在线程同步中使用信号量

    所谓线程同步,就是多个线程之间在某个对象上执行等待(也可理解为锁定该对象),直到该对象被解除锁定.C#中对象的类型分为引用类型和值类型.CLR在这两种类型上的等待是不一样的.我们可以简单的理解为在CL ...

  3. 【转】多线程:C#线程同步lock,Monitor,Mutex,同步事件和等待句柄(上)

    本篇从Monitor,Mutex,ManualResetEvent,AutoResetEvent,WaitHandler的类关系图开始,希望通过 本篇的介绍能对常见的线程同步方法有一个整体的认识,而对 ...

  4. Python GUI之tkinter窗口视窗教程大集合(看这篇就够了) JAVA日志的前世今生 .NET MVC采用SignalR更新在线用户数 C#多线程编程系列(五)- 使用任务并行库 C#多线程编程系列(三)- 线程同步 C#多线程编程系列(二)- 线程基础 C#多线程编程系列(一)- 简介

    Python GUI之tkinter窗口视窗教程大集合(看这篇就够了) 一.前言 由于本篇文章较长,所以下面给出内容目录方便跳转阅读,当然也可以用博客页面最右侧的文章目录导航栏进行跳转查阅. 一.前言 ...

  5. 改善C#程序的建议6:在线程同步中使用信号量

    原文:改善C#程序的建议6:在线程同步中使用信号量 所谓线程同步,就是多个线程之间在某个对象上执行等待(也可理解为锁定该对象),直到该对象被解除锁定.C#中对象的类型分为引用类型和值类型.CLR在这两 ...

  6. Linux 系统编程 学习:11-线程:线程同步

    Linux 系统编程 学习:11-线程:线程同步 背景 上一讲 我们介绍了线程的属性 有关设置.这一讲我们来看线程之间是如何同步的. 额外安装有关的man手册: sudo apt-get instal ...

  7. [ 高并发]Java高并发编程系列第二篇--线程同步

    高并发,听起来高大上的一个词汇,在身处于互联网潮的社会大趋势下,高并发赋予了更多的传奇色彩.首先,我们可以看到很多招聘中,会提到有高并发项目者优先.高并发,意味着,你的前雇主,有很大的业务层面的需求, ...

  8. C#多线程之线程同步篇3

    在上一篇C#多线程之线程同步篇2中,我们主要学习了AutoResetEvent构造.ManualResetEventSlim构造和CountdownEvent构造,在这一篇中,我们将学习Barrier ...

  9. C#多线程之线程同步篇2

    在上一篇C#多线程之线程同步篇1中,我们主要学习了执行基本的原子操作.使用Mutex构造以及SemaphoreSlim构造,在这一篇中我们主要学习如何使用AutoResetEvent构造.Manual ...

随机推荐

  1. Java中Comparable与Comparator的区别

    相同 Comparable和Comparator都是用来实现对象的比较.排序 要想对象比较.排序,都需要实现Comparable或Comparator接口 Comparable和Comparator都 ...

  2. javascript设计模式:策略模式

    前言 策略模式有效利用组合.委托.多态等技术和思想,可以有效避免多重条件选择语句. 策略模式对开放-封闭原则提供了很好的支持,将算法封装在strategy中,使得他们易于切换.理解.扩展. 策略模式中 ...

  3. 神技!微信小程序(应用号)抢先入门教程(附最新案例DEMO-豆瓣电影)持续更新

    微信小程序 Demo(豆瓣电影) 由于时间的关系,没有办法写一个完整的说明,后续配合一些视频资料,请持续关注 官方文档:https://mp.weixin.qq.com/debug/wxadoc/de ...

  4. Create a Team in RHEL7

    SOLUTION VERIFIED September 13 2016 KB2620131 Environment Red Hat Enterprise Linux 7 NetworkManager ...

  5. Java 教程整理:基础、项目全都有

    Java 在编程语言排行榜中一直位列前排,可知 Java 语言的受欢迎程度了. 网上有很多 Java 教程,无论是基础入门还是开发小项目的教程都比比皆是,可是系统的很少,对于Java 学习者来说找到系 ...

  6. Hibernate 系列 学习笔记 目录 (持续更新...)

    前言: 最近也在学习Hibernate,遇到的问题差不多都解决了,顺便把学习过程遇到的问题和查找的资料文档都整理了一下分享出来,也算是能帮助更多的朋友们了. 最开始使用的是经典的MyEclipse,后 ...

  7. Spring MVC数据校验

    在web应用程序中,为了防止客户端传来的数据引发程序异常,常常需要对 数据进行验证.输入验证分为客户端验证与服务器端验证.客户端验证主要通过JavaScript脚本进行,而服务器端验证则主要通过Jav ...

  8. 您真的理解了SQLSERVER的日志链了吗?

    您真的理解了SQLSERVER的日志链了吗? 先感谢宋沄剑给本人指点迷津,还有郭忠辉童鞋今天在QQ群里抛出的问题 这个问题跟宋沄剑讨论了三天,再次感谢宋沄剑 一直以来,SQLSERVER提供了一个非常 ...

  9. 我的MYSQL学习心得(十六) 优化

    我的MYSQL学习心得(十六) 优化 我的MYSQL学习心得(一) 简单语法 我的MYSQL学习心得(二) 数据类型宽度 我的MYSQL学习心得(三) 查看字段长度 我的MYSQL学习心得(四) 数据 ...

  10. 用ProGet搭建内部的NuGet服务器

    最近团队内部用的一个很简陋的NuGet服务器出问题了,nuget push发包,客户端显示发布成功,服务器上就是没有.懶得再去排查这个问题,早就想换掉这个过于简陋的NuGet服务器,借此机会直接弃旧迎 ...