• 线程基础之线程同步


    一、什么是线程同步

      1:线程同步是 多线程中时通共享资源的常用技术 当我们遇到多个线程使用单一对象所造成的竞争条件,如何顺序正确的取执行他称之为线程同步

      2:内核模式,当线程a占用共享对象,使其他线程置于阻塞状态 会造成上下文切换(操作系统的线程调度器。保存等待线程,并切换到另一个线程,依次恢复等待线程)

            将会消耗性能

      3:用户模式,简单的等待,不降线程切换到阻塞状态, 线程等待会浪费cpu时间,但是节省了上下文切换耗费的cpu时间 ,该模式轻,快,适合短时间的线程

      4:混合模式:先尝试用户模式,如果时间长就切换内核模式,以节省cpu资源

    二、为什么使用线程同步

      1:线程同步可以解决多个线程共享一个对象所造成竞争,死锁等复杂的问题

    三、怎么使用线程同步

      1:使用线程的原子操作(一个线程只占用一个量子的时间,一次就可以完成)无需其他线程等待当前操作wanc,就避免使用锁,也就避免造成死锁,从而不用阻塞线程就可避免竞争条件

     1 using System;
     2 using System.Threading;
     3 using static System.Console;
     4 
     5 namespace Chapter2.Recipe1
     6 {
     7     internal class Program
     8     {
     9         private static void Main(string[] args)
    10         {
    11             WriteLine("Incorrect counter");
    12             //反面例子:创建三个线程调用TestCounter方法来执行递增递减操作,线程是不安全的,将会造成竞争条件,运行多次将会得到不是非0的结果
    13             var c = new Counter();
    14             var t1 = new Thread(() => TestCounter(c));
    15             var t2 = new Thread(() => TestCounter(c));
    16             var t3 = new Thread(() => TestCounter(c));
    17             t1.Start();
    18             t2.Start();
    19             t3.Start();
    20             t1.Join();
    21             t2.Join();
    22             t3.Join();
    23 
    24             WriteLine($"Total count: {c.Count}");
    25 
    26             WriteLine("Correct counter");
    27             //正面例子:使用Interlocked为多个线程共享变量提供原子性操作
    28             var c1 = new CounterNoLock();
    29 
    30             t1 = new Thread(() => TestCounter(c1));
    31             t2 = new Thread(() => TestCounter(c1));
    32             t3 = new Thread(() => TestCounter(c1));
    33             t1.Start();
    34             t2.Start();
    35             t3.Start();
    36             t1.Join();
    37             t2.Join();
    38             t3.Join();
    39 
    40             WriteLine($"Total count: {c1.Count}");
    41             ReadLine();
    42         }
    43         
    44         static void TestCounter(CounterBase c)
    45         {
    46             for (int i = 0; i < 100000; i++)
    47             {
    48                 c.Incrementa();
    49                 c.Decrement();
    50             }
    51         }
    52         //反面例子 
    53         class Counter : CounterBase
    54         {
    55             private int _count;
    56 
    57             public int Count => _count;
    58 
    59             public override void Incrementa()
    60             {
    61                 _count++;
    62             }
    63 
    64             public override void Decrement()
    65             {
    66                 _count--;
    67             }
    68         }
    69 
    70         class CounterNoLock : CounterBase
    71         {
    72             private int _count;
    73 
    74             public int Count => _count;
    75 
    76             public override void Incrementa()
    77             {
    78                 Interlocked.Increment(ref _count);
    79             }
    80 
    81             public override void Decrement()
    82             {
    83                 Interlocked.Decrement(ref _count);
    84             }
    85         }
    86 
    87         abstract class CounterBase
    88         {
    89             public abstract void Incrementa();
    90 
    91             public abstract void Decrement();
    92         }
    93     }
    94 }
    原子性操作

     Mutex类

      含义:一种原始的同步方法,只对一个线程授予共享资源的独占访问

           用法:可用于不同程序中同步线程,可在大量的使用场景中, 如果当需要多线程写入的的时候,可考虑

      使用详细:https://www.cnblogs.com/Tench/p/7710994.html

    SemaphoreSlim

      含义:限制了同时访问同一个资源线程的数量,

    AutoResetEvent

      含义:信号类,从一个线程向另一个线程发送通知。某线程通知等待的线程有某件事发生

    ManuaIResetEventSlim

      含义:信号类,在线程之间传递信号

    using System;
    using System.Threading;
    using static System.Console;
    using static System.Threading.Thread;
    
    namespace Chapter2.Recipe5
    {
        class Program
        {
            //_mainEvent.Set(); 开门     _mainEvent.Reset();关门
            //ABC三个人发现了一个规律,某个ATM凌晨的时候 5s会吐一次钱,开心的要死
            //凌晨一到, A提前一秒就到了ATM钱,果然,ATM吐钱了,A开心的走了 
            //B离家远 9秒钟才到,果然10秒钟的时候又吐钱了
            //C和b住一起,但是没打到滴滴 于是想只要等五秒就可以得到钱钱 开心的要死 谁知道那个龟孙子 _mainEvent.Reset()设置了 999999秒 
            static void Main(string[] args)
            {
                var t1 = new Thread(() => TravelThroughGates("a", 4));
                var t2 = new Thread(() => TravelThroughGates("b", 9));
                var t3 = new Thread(() => TravelThroughGates("c", 10));
                t1.Start();
                t2.Start();
                t3.Start();
                Sleep(TimeSpan.FromSeconds(5));
                WriteLine(" ATM吐钱");
                _mainEvent.Set();
                _mainEvent.Set();
                _mainEvent.Set();
                Sleep(TimeSpan.FromSeconds(5));
                _mainEvent.Reset();
                WriteLine(" ATM关闭");
                Sleep(TimeSpan.FromSeconds(10));
                WriteLine(" ATM关闭吐钱");
                _mainEvent.Set();
                Sleep(TimeSpan.FromSeconds(999999));
                WriteLine(" ATM关闭");
                _mainEvent.Reset();
            }
    
            static void TravelThroughGates(string threadName, int seconds)
            {
                WriteLine($"{threadName}  到了");
                Sleep(TimeSpan.FromSeconds(seconds));
                WriteLine($"{threadName} 等待吐钱");
                _mainEvent.Wait();
                WriteLine($"{threadName} 拿到钱");
            }
    
            static ManualResetEventSlim _mainEvent = new ManualResetEventSlim(false);
        }
    }
    ATM吐钱例子

    CountDownEvent

      含义:信号类,等待一定数量线程的操作完成,后去执行某些事情

     1 using System;
     2 using System.Threading;
     3 using static System.Console;
     4 using static System.Threading.Thread;
     5 
     6 namespace Chapter2.Recipe6
     7 {
     8     class Program
     9     {//把PerformOperation当做多线程使用的一个共享资源,一个5s,一个20s,只有当线程一起执行过2次之后,主程序才会继续走下去,没达到次数将会一直等待
    10         static void Main(string[] args)
    11         {
    12             WriteLine("Starting two operations");
    13             var t1 = new Thread(() => PerformOperation("Operation 1 is completed", 5));
    14             var t2 = new Thread(() => PerformOperation("Operation 2 is completed", 20));
    15             t1.Start();
    16             t2.Start();
    17             _countdown.Wait();
    18             WriteLine("Both operations have been completed.");
    19             _countdown.Dispose();
    20         }
    21 
    22         static CountdownEvent _countdown = new CountdownEvent(2);//设置次数
    23 
    24         static void PerformOperation(string message, int seconds)
    25         {
    26             Sleep(TimeSpan.FromSeconds(seconds));
    27             WriteLine(message);
    28             _countdown.Signal();
    29         }
    30     }
    31 }
    例子详解

    Barrier

      含义:用于组织多个线程及时在某个时刻碰面,提供一个回调函数,每次线程调用了SignalAndWait方法后改回调函数会被执行

      用法:多线程迭代中非常有用,可在每个迭代钱执行一些计算

     1 using System;
     2 using System.Threading;
     3 using static System.Console;
     4 using static System.Threading.Thread;
     5 
     6 namespace Chapter2.Recipe7
     7 {
     8     class Program
     9     {
    10         //吉他手和歌手一起演奏, 但是歌手2秒就完成,于是触发SignalAndWait方法, 我唱到了第几次
    11         static void Main(string[] args)
    12         {
    13             var t1 = new Thread(() => PlayMusic("吉他手", "演奏", 10));
    14             var t2 = new Thread(() => PlayMusic("歌手", "唱歌", 2));
    15 
    16             t1.Start();
    17             t2.Start();
    18             Console.ReadLine();
    19         }
    20 
    21         static Barrier _barrier = new Barrier(1,
    22     b => WriteLine($"阶段完成{b.CurrentPhaseNumber + 1}"));
    23 
    24         static void PlayMusic(string name, string message, int seconds)
    25         {
    26             for (int i = 0; i < 3; i++)
    27             {
    28                 WriteLine(name+"----------------------开始------------------------");
    29                 Sleep(TimeSpan.FromSeconds(seconds));
    30                 WriteLine($"{name} 开始 to {message}");
    31                 Sleep(TimeSpan.FromSeconds(seconds));
    32                 WriteLine($"{name} 结束 to {message}");
    33                 _barrier.SignalAndWait();
    34             }
    35         }
    36     }
    37 }
    列子详解
  • 相关阅读:
    RecyclerView中装饰者模式应用
    Android中的Drawable和动画
    Android的线程和线程池
    Bitmap的加载和Cache
    Android的消息机制
    【Java基础】线程和并发机制
    Asp.Net 将HTML中通过dom-to-image.js标签div内的内容转化为图片保存到本地
    Asp.Net MVC @Html.TextBox 只允许输入数字问题
    程序员编程10大原则,请牢牢记住
    Asp.Net MVC WebAPI的创建与前台Jquery ajax后台HttpClient调用详解
  • 原文地址:https://www.cnblogs.com/LZXX/p/11858157.html
Copyright © 2020-2023  润新知