当前位置: 首页 > 编程笔记 >

C#线程同步的三类情景分析

陆斌
2023-03-14
本文向大家介绍C#线程同步的三类情景分析,包括了C#线程同步的三类情景分析的使用技巧和注意事项,需要的朋友参考一下

本文实例讲述了C#线程同步的三类情景,分享给大家供大家参考。具体分析如下:

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(1000);//故意延迟从而保证第二个线程是在第一个线程之后才执行         Task.Run(() =>         {             Console.WriteLine("步骤一");             autoReset.Set();         });     }

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

如果用 lock 呢?

    private void button1_Click(object sender, EventArgs e)

    {

        Task.Run(() =>

        {

            lock (s)

            {

                Console.WriteLine("步骤一");

            }

        });

 

        Thread.Sleep(1000);//必须人为确保步骤二的线程要在步骤一的线程之后执行

        Task.Run(() =>

        {

            lock (s)

            {

                Console.WriteLine("步骤二");

            }

        });

    }

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

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

总 结

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

希望本文所述对大家的C#程序设计有所帮助。

 类似资料:
  • 本文向大家介绍深入分析C# 线程同步,包括了深入分析C# 线程同步的使用技巧和注意事项,需要的朋友参考一下 上一篇介绍了如何开启线程,线程间相互传递参数,及线程中本地变量和全局共享变量区别。 本篇主要说明线程同步。 如果有多个线程同时访问共享数据的时候,就必须要用线程同步,防止共享数据被破坏。如果多个线程不会同时访问共享数据,可以不用线程同步。 线程同步也会有一些问题存在: 性能损耗。获取,释放锁

  • 问题内容: 在面试中被问到这个问题,试图解决…但是没有成功。我想到了使用CyclicBarrier 有三个线程T1打印1,4,7 … T2打印2,5,8 …,T3打印3,6,9…。您如何同步这三个来打印序列1,2,3,4,5,6,7,8,9…。 我尝试编写并运行以下代码 输出 谁能帮助我纠正错误? 类似的 线程同步查询-同步三个线程以打印012012012012.....无法正常工作 问题答案:

  • 在采访中被问到这个问题,试图解决它。。。但并不成功。我想用自行车旅行车 有三个线程T1打印1,4,7... T2打印2,5,8......和T3打印3,6,9......你如何将这三个同步到打印序列1,2,3,4,5,6,7,8,9...... 我试着写作 输出 有人能帮我纠正错误吗? 类似的Ques线程同步-将三个线程同步到打印012。。。。。不起作用

  • 本文向大家介绍浅析c# 线程同步,包括了浅析c# 线程同步的使用技巧和注意事项,需要的朋友参考一下 同步是一种只允许一个线程在特定时间访问某些资源的技术。没有其他线程可以中断,直到所分配的线程或当前访问线程访问数据完成其任务。 在多线程程序中,允许线程访问任何资源所需的执行时间。线程共享资源并异步执行。 访问共享资源(数据)是有时可能会暂停系统的关键任务。所以可以通过线程同步来处理它。 主要场景如

  • 本文向大家介绍C# 线程同步的方法,包括了C# 线程同步的方法的使用技巧和注意事项,需要的朋友参考一下 一、进程内部的线程同步 1、使用lock,用法如下: 特性:只能传递对象,无法设置等待超时 2、使用:InterLocked(原子操作) 其在System.Threading命名空间下,Interlocked实际是类控制计数器,从而实现进程的同步,其很容易实现生产者消费者模型 3、使用Monit

  • 本文向大家介绍C#中线程同步对象的方法分析,包括了C#中线程同步对象的方法分析的使用技巧和注意事项,需要的朋友参考一下 本文实例讲述了C#中线程同步对象的方法。分享给大家供大家参考。具体分析如下: 在编写多线程程序时无可避免会遇到线程的同步问题。什么是线程的同步呢? 举个例子:如果在一个公司里面有一个变量记录某人T的工资count=100,有两个主管A和B(即工作线程)在早一些时候拿了这个变量的值