• C#(四十二)之线程同步、互锁


    无关线程:线程之间没有任何联系,独立运行,互不干扰

    相关线程:线程之间有联系,两个线程之间资源共享

    临界线程:多个线程共享资源

    临界区:访问临界资源代码

    同步:两个线程协同工作才能完成同一项任务

    相关线程实例:

    public static char buffer;
            public static string str;
            static void Main(string[] args)
            {
                // 开写线程
                Thread th = new Thread(write);
                th.Start();
     
                // 开度线程
                Thread re = new Thread(delegate() {
                    for (int i = 0; i < str.Length; i++)
                    {
                        char ch = buffer;
                        Console.Write(ch); // 醒木非根,半风走一,谈疏君莫,是一说人人人人人人人人人人人人人
                        Thread.Sleep(50);
                    }
                });
                re.Start();
     
                Console.ReadLine();
            }
            /// 
            /// 写入
            /// 
            public static void write()
            {
                str = "醒木本非同根生,半生风雨走一程,谈笑疏狂君莫问,却是一位说书人";
                for (int i = 0; i < str.Length; i++)
                {
                    buffer = str[i];
                    Thread.Sleep(30);
                }
     
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    打印结果:

    醒木非根,半风走一,谈疏君莫,是一说人人人人人人人人人人人人人


    造成这种情况的原因是:读写线程时间不相同。


    Interlocked(互锁):

    使用线程锁Interlocked来解决这个问题

    Interlocked的一些属性

    Interlocked.Increment(ref value) 数值加一(原子性操作)

    Interlocked.Decrement(ref value) 数值减一(原子性操作)

    Interlocked.Exchange(ref value1, value2) 交换:把值2赋给值1;返回新值

    Interlocked.CompareExchange(ref value1, value2, value3) 实现比较和交换两种功能:值1和值3比较,如果相同,把值2给值1,不相同则不作任何操作;返回原值(多用于判断条件)

    Interlocked.Read读取计数器的值

    Interlocked.Add使计数器增加指定的值

    使用Interlocked改造完成之后的代码:

    // 开写线程
                Thread th = new Thread(write);
                th.Start();
                // 开度线程
                Thread re = new Thread(delegate() {
                    for (int i = 0; i < str.Length; i++)
                    {
                        while (Interlocked.Read(ref num) == 0)
                        {
                            Thread.Sleep(10);
                        }
                        char ch = buffer;
                        Console.Write(ch);  
                        //Thread.Sleep(50);
                        // 使值减少1
                        Interlocked.Decrement(ref num);
                    }
                });
                re.Start();
                Console.ReadLine();
            }
            /// 
            /// 写入
            /// 
            public static void write()
            {
                str = "醒木本非同根生,半生风雨走一程,谈笑疏狂君莫问,却是一位说书人";
                for (int i = 0; i < str.Length; i++)
                {
                    while (Interlocked.Read(ref num) == 1)
                    {
                        Thread.Sleep(10);
                    }
                    buffer = str[i];
                    //Thread.Sleep(30);
                    // 使值增加1
                    Interlocked.Increment(ref num);
                }
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    Monitor(管程):

    配合try-catch-finally(需要退出exit)或者Lock使用

    锁定的对象应该声明为private static object obj = new object();尽量别用公共变量和字符串、this、值类型。


    属性和方法:

    Enter(Object) 在指定对象上获取排他锁。
    Exit(Object) 释放指定对象上的排他锁。
    IsEntered 确定当前线程是否保留指定对象锁。
    Pulse 通知等待队列中的线程锁定对象状态的更改。
    PulseAll 通知所有的等待线程对象状态的更改。
    TryEnter(Object) 试图获取指定对象的排他锁。
    TryEnter(Object, Boolean) 尝试获取指定对象上的排他锁,并自动设置一个值,指示是否得到了该锁。
    Wait(Object) 释放对象上的锁并阻止当前线程,直到它重新获取该锁。

    public static char buffer;
            public static string str;
            /// 
            /// 此变量为InterLocked使用
            /// 
            public static long num;
            /// 
            /// 此变量为Monitor 使用
            /// 
            public static object obj = new object();
            static void Main(string[] args)
            {
                // 开写线程
                Thread th = new Thread(write);
                th.Start();
                // 开度线程
                Thread re = new Thread(delegate() {
                    try
                    {
                        for (int i = 0; i < str.Length; i++)
                        {
                            /*while (Interlocked.Read(ref num) == 0)
                            {
                                Thread.Sleep(10);
                            }//*/
                            // 程序进入临界区,上锁
                            Monitor.Enter(obj);
                            char ch = buffer;
                            Console.Write(ch);  
                            // 通知等待队列中的线程锁定对象状态的更改
                            Monitor.Pulse(obj);
                            // 释放对象上的锁并阻止当前线程,直到它重新获取该锁
                            Monitor.Wait(obj);
                            /*//Thread.Sleep(50);
                            // 使值减少1
                            Interlocked.Decrement(ref num);//*/
                        }
                    }
                    catch (Exception)
                    {
                        throw;
                    }
                    finally
                    {
                        Monitor.Exit(obj);
                    }
                   
                });
                re.Start();
                Console.ReadLine();
            }
            /// 
            /// 写入
            /// 
            public static void write()
            {
                try
                {
                    str = "醒木本非同根生,半生风雨走一程,谈笑疏狂君莫问,却是一位说书人";
                    for (int i = 0; i < str.Length; i++)
                    {
                        /*while (Interlocked.Read(ref num) == 1)
                        {
                            Thread.Sleep(10);
                        }//*/
                        // 程序进入临界区,上锁
                        Monitor.Enter(obj);
                        buffer = str[i];
                        // 通知等待队列中的线程锁定对象状态的更改
                        Monitor.Pulse(obj);
                        // 释放对象上的锁并阻止当前线程,直到它重新获取该锁
                        Monitor.Wait(obj);
                        /*//Thread.Sleep(30);
                        // 使值增加1
                        Interlocked.Increment(ref num);//*/
                    }
                }
                catch (Exception)
                {
                    throw;
                }
                finally {
                    Monitor.Exit(obj);
                }
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85

    Lock:上锁

    Monitor和Lock的区别

    1.Lock是Monitor的语法糖。
    2.Lock只能针对引用类型加锁。
    3.Monitor能够对值类型进行加锁,实质上是Monitor.Enter(object)时对值类型装箱。
    4.Monitor还有其他的一些功能。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1FF05HWw-1658305676246)(https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/b7444c97a2c0455693d468fbdc8c09b8~tplv-k3u1fbpfcp-zoom-1.image “1559178866415958.png”)]

    使用lock代替try之后:程序变得简洁了一些:仅限于Monitor

    static void Main(string[] args)
            {
                // 开写线程
                Thread th = new Thread(write);
                th.Start();
               
                // 开度线程
                Thread re = new Thread(delegate() {
                    for (int i = 0; i < str.Length; i++)
                    {
                        lock (obj)
                        {
                            /*while (Interlocked.Read(ref num) == 0)
                            {
                                Thread.Sleep(10);
                            }//*/
                            char ch = buffer;
                            Console.Write(ch);
                            // 通知等待队列中的线程锁定对象状态的更改
                            Monitor.Pulse(obj);
                            // 释放对象上的锁并阻止当前线程,直到它重新获取该锁
                            Monitor.Wait(obj);
                            /*//Thread.Sleep(50);
                            // 使值减少1
                            Interlocked.Decrement(ref num);//*/
                        } 
                    }
                });
                re.Start();
     
                Console.ReadLine();
            }
            /// 
            /// 写入
            /// 
            public static void write()
            {
              
                str = "醒木本非同根生,半生风雨走一程,谈笑疏狂君莫问,却是一位说书人";
                for (int i = 0; i < str.Length; i++)
                {
                    lock(obj)
                    {
                        /*while (Interlocked.Read(ref num) == 1)
                        {
                            Thread.Sleep(10);
                        }//*/
                        buffer = str[i];
                        // 通知等待队列中的线程锁定对象状态的更改
                        Monitor.Pulse(obj);
                        // 释放对象上的锁并阻止当前线程,直到它重新获取该锁
                        Monitor.Wait(obj);
                        /*//Thread.Sleep(30);
                        // 使值增加1
                        Interlocked.Increment(ref num);//*/
                    }
                }
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58

    以上方法对资源消耗比较大,合理使用

    有好的建议,请在下方输入你的评论。

    欢迎访问个人博客
    https://guanchao.site

    欢迎访问小程序:

    在这里插入图片描述

  • 相关阅读:
    【Linux升级之路】0_Linux入门 | 环境搭建 | 使用常识 | 用户管理
    300美元就能“盗走”一辆车,这些车主要小心了
    如何快速安装JDK 1.8 以及配置环境变量
    关于crypto1_bs
    springboot+shiro+layuimini实现后台管理系统的权限控制(一)基础环境搭建
    Windows 11 Beta 预览版用户已经可以体验 Android 应用
    Chrome浏览器书签同步配置方法
    [读后感-Cayden原创] 读《1-2年纪,塑造孩子一生的关键》有感
    在行首,行尾添加文本,替换文本中的空格、制表符等
    elasticsearch-head安装及详细配置
  • 原文地址:https://blog.csdn.net/qq_39708228/article/details/125895167