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

简介: 本篇内容记录了无关线程、相关线程、临界线程、互锁等

QQ图片20220426162339.jpg

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


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


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


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


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


相关线程实例:


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();
        }
        /// <summary>
        /// 写入
        /// </summary>
        public static void write()
        {
            str = "醒木本非同根生,半生风雨走一程,谈笑疏狂君莫问,却是一位说书人";
            for (int i = 0; i < str.Length; i++)
            {
                buffer = str[i];
                Thread.Sleep(30);
            }
        }


打印结果:


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


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


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();
        }
        /// <summary>
        /// 写入
        /// </summary>
        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);
            }
        }

 

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;
        /// <summary>
        /// 此变量为InterLocked使用
        /// </summary>
        public static long num;
        /// <summary>
        /// 此变量为Monitor 使用
        /// </summary>
        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();
        }
        /// <summary>
        /// 写入
        /// </summary>
        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);
            }
        }

 

Lock:上锁


Monitor和Lock的区别


1.Lock是Monitor的语法糖。

2.Lock只能针对引用类型加锁。

3.Monitor能够对值类型进行加锁,实质上是Monitor.Enter(object)时对值类型装箱。

4.Monitor还有其他的一些功能。


QQ图片20220426162341.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();
        }
        /// <summary>
        /// 写入
        /// </summary>
        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);//*/
                }
            }
        }

 

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



目录
相关文章
|
6月前
|
SQL 开发框架 安全
C#编程与多线程处理
【4月更文挑战第21天】探索C#多线程处理,提升程序性能与响应性。了解C#中的Thread、Task类及Async/Await关键字,掌握线程同步与安全,实践并发计算、网络服务及UI优化。跟随未来发展趋势,利用C#打造高效应用。
197 3
|
6月前
|
安全 编译器 C#
C#学习相关系列之多线程---lock线程锁的用法
C#学习相关系列之多线程---lock线程锁的用法
|
6月前
|
并行计算 安全 Java
C# .NET面试系列四:多线程
<h2>多线程 #### 1. 根据线程安全的相关知识,分析以下代码,当调用 test 方法时 i > 10 时是否会引起死锁? 并简要说明理由。 ```c# public void test(int i) { lock(this) { if (i > 10) { i--; test(i); } } } ``` 在给定的代码中,不会发生死锁。死锁通常是由于两个或多个线程互相等待对方释放锁而无法继续执行的情况。在这个代码中,只有一个线程持有锁,且没有其他线程参与,因此不
386 3
|
6月前
|
Java 调度 C#
C#学习系列相关之多线程(一)----常用多线程方法总结
C#学习系列相关之多线程(一)----常用多线程方法总结
|
6月前
|
C#
C#学习相关系列之多线程---ConfigureAwait的用法
C#学习相关系列之多线程---ConfigureAwait的用法
115 0
|
6月前
|
C#
C#学习相关系列之多线程---TaskCompletionSource用法(八)
C#学习相关系列之多线程---TaskCompletionSource用法(八)
170 0
|
6月前
|
Java C#
C#学习系列相关之多线程(五)----线程池ThreadPool用法
C#学习系列相关之多线程(五)----线程池ThreadPool用法
|
3月前
|
数据采集 XML JavaScript
C# 中 ScrapySharp 的多线程下载策略
C# 中 ScrapySharp 的多线程下载策略
|
2月前
|
安全 数据库连接 API
C#一分钟浅谈:多线程编程入门
在现代软件开发中,多线程编程对于提升程序响应性和执行效率至关重要。本文从基础概念入手,详细探讨了C#中的多线程技术,包括线程创建、管理及常见问题的解决策略,如线程安全、死锁和资源泄露等,并通过具体示例帮助读者理解和应用这些技巧,适合初学者快速掌握C#多线程编程。
77 0