C#(五十三)之线程同步、互锁

news2024/11/28 12:38:18

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

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

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

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

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

相关线程实例:

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还有其他的一些功能。

34324.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);//*/
                }
            }
        }

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

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

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/723810.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

单摆模型(博途PLC和Simulink仿真对比)

单摆模型的详细推导公式可以参看下面文章链接,这篇博客主要给出在博途PLC里如何完成单摆模型的建模, 倒立摆的PLC控制(模型分析+ SCL源代码)_RXXW_Dor的博客-CSDN博客首先简单介绍下倒立摆模型,下面这幅图是MATLAB网站上的倒立摆模型,下面我们利用牛顿第二运动定律建立摆杆…

自由创新,分享图片编辑工具

在当今社交媒体的流行时代&#xff0c;分享精美照片已成为人们生活中不可或缺的一部分。为了让我们的照片更加与众不同&#xff0c;图片编辑工具成为了必备的利器。这些神奇的工具不仅能帮助我们改善照片的外观和质量&#xff0c;还能增添创意效果&#xff0c;让我们的照片在社…

vue-导入图标iconfont到项目中并使用

阿里巴巴矢量图标库官网&#xff1a; 打开官网-我的素材库 选中要导入的项目-点击下载 下载好一个包 解压后得到 将这些文件全部复制到vue项目的src/assets/icon目录下 在main.js中导入 import /assets/icon/iconfont.css 即可使用 【示例】 在任意一个vue界面的<templa…

Loki 日志块使用 MinIO 对象存储

简介 与其他日志记录系统不同&#xff0c;Grafana Loki 是围绕仅索引有关日志的元数据的想法构建的&#xff1a;标签&#xff08;就像 Prometheus 标签一样&#xff09;。然后&#xff0c;日志数据本身会被压缩并以块的形式存储在对象存储&#xff08;例如 S3 或 GCS&#xff…

RealEvo-IDE 更换激活码 License到期续期

license到期之后&#xff0c;会出现RealEvo-IDE无法打开的状况。在有新license的状况下&#xff0c;如何在不重新安装的情况下使用IDE&#xff1f;主要分为以下几个步骤&#xff1a; &#xff08;1&#xff09; 右击“RealEvo-IDE”&#xff0c;点击“打开文件位置”&#xff…

软件测试技能,JMeter压力测试教程,逻辑控制器之事务控制器(二十五)

一、前言 TPS就是每秒钟所处理的事务数&#xff0c;那么到底什么是事务呢&#xff1f; 事务是用户自定义的一个标识&#xff0c;是一个或多个操作完成一个业务所花费的时间&#xff0c;事务时间反映的是一个操作过程的响应时间 二、事务场景 电商场景大家并不陌生&#xff…

【Unity】Unity接入内购IAP,提示you are not authorized to set the license key

接入IAP的时候需要输入谷歌的开发者后台key Unity2020之后有可能会提示&#xff1a;you are not authorized to set the license key 查阅相关内容后&#xff08;https://forum.unity.com/threads/purchase-you-are-not-authorized-to-set-the-license-key-google-play.95426…

vue运行background-removal-js库

前段时间在github上有个能在浏览器扣图的js库蛮火https://github.com/imgly/background-removal-js 演示网站&#xff1a;demo 不仅仅能将人物主体扣出&#xff0c;还能处理动物、植物等 最近我萌发出在vue中运行这个库的想法&#xff0c;记录一下 下载、配置 首先当然是通…

揭秘python函数:编程艺术的核心力量

文章目录 前言什么是 python 函数函数的使用步骤1&#xff09;定义函数2&#xff09;调用函数 带有参数的函数函数的返回值函数的说明文档函数的嵌套调用实现简易的计算器 前言 当我们深入研究 Python 的内心深处&#xff0c;我们将会发现&#xff0c;函数是其内核的核心力量。…

信息安全管理与评估赛题第5套

全国职业院校技能大赛 高等职业教育组 信息安全管理与评估 赛题五 模块一 网络平台搭建与设备安全防护<

(四)并发编程带来了哪些问题?

&#xff08;四&#xff09;并发编程带来了哪些问题&#xff1f; 4.1 引入4.2 线程安全问题01、原子性02、可见性 4.3 活跃性问题01、死锁02、活锁03、饥饿 4.4 性能问题 4.1 引入 在一定场景下&#xff0c;使用多线程会给我们日常工作带来很多的便利&#xff0c;但并不是在任…

记一次数据库迁移(迁移数据)

book,由于之前建表没注意字符集的问题&#xff0c;导致之前写入的数据出现乱码。现在要将之前的数据和现在数据的字符集一致&#xff0c;不出现乱码情况&#xff0c;将字符集为 latin1 已有记录的数据转成 utf8&#xff0c;并且已经存在的记录不乱码。 操作步骤&#xff1a; 建…

SPI接口调试

本文记录了复旦微fmql45t900 SPI裸核和linux系统下spi接口的调试步骤。 问题描述&#xff1a; 复旦微fmql45t900 SPI接口片选信号无法拉低控制。 原因分析&#xff1a; 为了排除硬件问题&#xff0c;创建spi裸核测试工程进行单步调试&#xff0c;spi发送数据时用示波器可以正…

FPGA USB FX2 图片发送试验 驱动CY7C68013A实现 提供2套工程源码和技术支持

目录 1、前言2、我这儿已有的 FPGA USB 通信方案3、CY7C68013A芯片解读和硬件设计FX2 简介SlaveFIFO模式及其配置 4、工程详细设计方案输入测试图片的处理PC上位机发送测试图片图像接收与缓存图像输出显示 5、vivado工程1--LCD输出显示6、vivado工程1--LCD输出显示7、上板调试验…

vue数组深层赋值

一、问题 使用vue开发的项目&#xff0c;有一个页面的data中定义了两个数组&#xff0c;需要把其中一个数组赋值给另一个数组的其中一个对象的一个属性&#xff08;有点拗口&#xff09;&#xff0c;如图所示&#xff1a; 二、错误赋值 直接使用opts:this.print_name&#xf…

el-button与i标签实现区域切换效果

题记&#xff1a;我们工作中需要实现内容区域切换放大缩小的效果&#xff0c;其实道理很简单&#xff0c;给事件给样式即可。 #el-button与i标签实现区域切换效果 ##图片展示&#xff1a; ##代码实现 ###template部分 <div class"right"><el-button type&…

【SQL应知应会】行列转换(三)• Oracle版

欢迎来到爱书不爱输的程序猿的博客, 本博客致力于知识分享&#xff0c;与更多的人进行学习交流 本文收录于SQL应知应会专栏,本专栏主要用于记录对于数据库的一些学习&#xff0c;有基础也有进阶&#xff0c;有MySQL也有Oracle 行列转换 • Oracle版 oracle的行列转换前言1.数据…

Perhaps you are running on a JRE rather than a JDK?

我记得我遇到过好多次 mvn clean package的时候报错&#xff1b; 最后检查时这里的路径配置错了

蓝牙耳机品牌排行榜前十名!2023年超全蓝牙耳机合集!

虽然称不上发烧友&#xff0c;但近年来用过的蓝牙耳机少说也有几十款了。这篇文章以近年来市面上的热销且评价都比较高的十款蓝牙耳机为主要推荐对象&#xff0c;来给大家做一期有关于蓝牙耳机的实测体验以及提供相关的选购思路&#xff0c;希望能为大家的选择出一份力。 第一…

# TSWIKI 0.2 版本

TSWIKI 0.2 版本 TSWIKI 0.2 版本经过这段时间的改进&#xff0c;增加了搜索&#xff0c;版本变更查看等功能&#xff0c;基本开发完成。功能方面已经与 gollum 类似。 功能改进说明 1、GIT 版本变更查看功能 实现 git 版本变更查看功能, 在 markdown 文档查看界面&#xf…