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

news/2024/10/30 1:09:14/

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

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

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

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

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

相关线程实例:

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);// 使值减少1Interlocked.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);// 使值增加1Interlocked.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);// 使值减少1Interlocked.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);// 使值增加1Interlocked.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);// 使值减少1Interlocked.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);// 使值增加1Interlocked.Increment(ref num);//*/}}}

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

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

在这里插入图片描述


http://www.ppmy.cn/news/747536.html

相关文章

mysql数据库-链接密码加密

# 此种方式可以&#xff0c;隐藏明文密码&#xff0c;druid方案 # 参考资料&#xff1a; ## https://blog.csdn.net/m0_46360888/article/details/128369783 ## https://blog.csdn.net/xiha_zhu/article/details/126423798## 注意事项 #### 1、要配置 spring.datasource.druid …

小技巧:visio 如何让箭头完全水平

有时候我们绘制的水平箭头&#xff0c;粗看是水平的&#xff0c;放大来看&#xff0c;发现有一些锯齿&#xff0c;不是完全水平的&#xff0c;怎么办呢&#xff1f; 开始 -> 排列 -> 垂直居中&#xff0c; OK&#xff0c;即使放大来看&#xff0c;仍然是非常平的一条线&a…

visio2013如何画箭头

第一步&#xff0c;如下图所示&#xff0c;在visio中点击插入&#xff0c;然后点击连接线&#xff0c;在需要连接线的地方画上&#xff1b; 第二步&#xff0c;点击该连接线&#xff0c;右键&#xff0c;选择 “设置形状格式&#xff08;S&#xff09;”&#xff0c;即可在右边…

Visio 流程图的箭头 设置

1、点击选中连接线 2、点击线条下拉三角符号&#xff0c;然后选择箭头&#xff0c;如下 3、设置好箭头后&#xff0c;还可以使用格式刷来格式化其他没加箭头的&#xff0c;箭头会加到你拖动连接结束的地方 【句子能量】也许你的原创句子也不错哦&#xff0c;学习工作之余来发上…

如何用Visio画出总线(空心的箭头)

在visio中可以首先画出一个空白的箭头&#xff0c;在下图中找到。 画出一个箭头 然后可以在这个箭头后面拼接长方体&#xff0c;如图 然后&#xff0c;选择开发工具中的操作的联合 得到的效果如下 这样&#xff0c;就画出了总线。

visio一分二的箭头_Visio如何画各种线条与箭头?

Office Visio 是一个强大的绘图软件, 在IT行业及领域中经常会用到, 用于处理流程环节可视化处理与分析和交流。并且能够创建具有专业外观的图表&#xff0c;以便理解、记录和分析。 1、打开Visio文件&#xff0c;我们在功能区域的最上方会看到很多的功能&#xff0c;在如图标注…

visio如何画程序流程图的箭头

转载&#xff1a;https://jingyan.baidu.com/article/73c3ce2841be3ee50343d92a.html 传送门

visio一分二的箭头_visio双箭头怎么画? visio2013绘制双箭头直线的教程

microsoft visio是微软公司开发的一款专用的绘图软件&#xff0c;使用该软件可以绘制组织结构图&#xff0c;网络拓扑图&#xff0c;流程图等&#xff0c;尤其使用该软件绘制组织结构图时&#xff0c;可能需要用到双箭头直线&#xff0c;双箭头虚线等&#xff0c;那么使用visio…