C# 线程问题之争用条件

5fb6df8b042b3a2db6fd239a2a5e58d9.png

用多个线程编程并不容易。在启动访问相同数据的多个线程时,会间歇性地遇到难以发现的问题。如果使用任务、并行 LINQ 或 Parallel 类,也会遇到这些问题。为了避免这些问题,必须特别注意同步问题和多个线程可能发生的其他问题。下面探讨与线程相关的问题争用条件。

ThreadingIssues示例的代码使用了如下名称空间: 

System.Diagnostics 

System.Threading

System.Threading.Tasks

static System.Console

可以使用命令行参数启动 ThreadingIssues示例应用程序,来模拟争用条件。

e61bb77bfcdf2fa19c251d761ae5939f.png

如果两个或多个线程访问相同的对象,并且对共享状态的访问没有同步,就会出现争用条件。为了说明争用条件,下面的例子定义一个 StateObject 类,它包含一个 int 字段和一个 ChangeState() 方法。在 ChangeState() 方法的实现代码中,验证状态变量是否包含5。如果它包含,就递增其值。下一条语句是Trace.Assert,它立刻验证 state 现在是包含 6。

435633d7cf5f86fe63d5fc819a539f5c.png

在给包含 5 的变量递增了 1 后,可能认为该变量的值就是 6。但事实不一定是这样。例如,如果一个线程刚刚执行完  if(_state==5)语句,它就被其他线程抢占,调度器运行另一个线程。第二个线程现在进入 if 体,因为 state 的值仍是 5,所以将它递增到 6。第一个线程现在再次被调度,在下一条语句中,State 递增到 7。这时就发生了争用条件,并显示断言消息:

public class StateObject
{private int _state = 5;public void ChangeState(int loop) 
{if (_state == 5){_state++;If (_state != 6){Console.WriteLine($"Race conditon occurred after {loop} loops"); Trace.Fail("race condition");}}_state = 5;}
}

dab4e7fdd16f1484f8628ecda447f7c2.png

下面通过给任务定义一个方法来验证这一点。SampleTask 类的 RaceCondition()方法将一个 StateObject 类作参数。在一个无限while循环中,调用ChangeState() 方法。变量 i 仅用于显示断言消息中的循环次数。

public class SampleTask
{public void RaceCondition(object o){Trace.Assert(o is StateObject, "o must be of type StateObject"); StateObject state = o as StateObject;int i = 0; while (true){state.ChangeState(i++);}}
}

7c0c8d59d3ccb3c628ef99f828e8eb4a.png

在程序的 Main() 方法中,新建了一个 StateObject 对象,它由所有任务共享。通过使用传递给 Task 的 Run 方法的 lambda 表达式调用 RaceCondition 方法来创建 Task 对象。然后,主线程等待用户输入。但是,因为可能出现争用,所以程序很有可能在读取用户输入前就挂起:

public void RaceConditions()
{var state = new StateObject(); for (int i = 0; i < 2; i++){Task.Run(() => new SampleTask().RaceCondition(state));}
}

ffe0d217d9f36080599ed7619d409ddb.png

启动程序,就会出现争用条件。多久以后出现第一个争用条件要取决于系统以及将程序构建为发布版本还是调试版本。如果构建为发布版本,该问题的出现次数就会比较多,因为代码被优化了。如果系统中有多个 CPU 或使用双核/四核 CPU,其中多个线程可以同时运行,则该问题也会比单核 CPU 的出现次数多。在单核CPU中,因为线程调度是抢占式的,也会出现该问题,只是没有那么频繁。

b012637add9048cbc98478e5dcb53b34.png

在我的系统上运行程序时,显示在 85232 个循环后出现错误;在另一次运行程序时,显示在 70037 个循环后出现错误。多次启动应用程序,总是会得到不同的结果。

779e479743447bf505bb4f64c992543e.png

要避免该问题,可以锁定共享的对象。这可以在线程中完成:用下面的 lock 语句锁定在线程中共享的 state 变量。只有一个线程能在锁定块中处理共享的 state 对象。由于这个对象在所有的线程之间共享,因此,如果一个线程锁定了 state,另一个线程就必须等待该锁定的解除。一旦接受锁定,线程就拥有该锁定,直到该锁定块的末尾才解除锁定。如果改变 state 变量引用的对象的每个线程都使用一个锁定,就不会出现争用条件。

public class SampleTask
{public void RaceCondition(object o){Trace.Assert(o is StateObject, "o must be of type StateObject"); StateObject state = o as StateObject; int t = 0;while (true){lock (state) // no race condition with this lock{state.ChangeState(i++);}}}
}

7b08a40ec02a1ced76ef8f5b14f5df19.png

注意:

在下载的示例代码中,需要取消锁定语句的注释,才能解决争用条件的问题。

95159ac242eb50dc0efce420bd6afaf0.png

在使用共享对象时,除了进行锁定之外,还可以将共享对象设置为线程安全的对象。在下面的代码中, ChangeState() 方法包含一条 lock 语句。由于不能锁定 state 变量本身(只有引用类型才能用于锁定),因此定义一个object 类型的变量 sync,将它用于lock 语句。如果每次 state 的值更改时,都使用同一个同步对象来锁定,就不会出现争用条件。

public class StateObject
{private int state = 5;private _object sync = new object(); public void ChangeState(int loop) {lock (_sync){if (_state == 5){_state++;if (_state != 6){Console.WriteLine($"Race condition occured after {loop} loops");Trace.Fail($"race condition at {loop}");}}_state = 5;}}}

a2f01648c48d9948289de1324ff10939.png

30c8844a69a63a166ca2bf1ec6c0a0aa.png

 微信公众号 

Dotnet讲堂

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

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

相关文章

尾调用优化 java_为什么JVM仍然不支持尾调用优化?

拉丁的传说也许您已经知道这一点&#xff0c;但是这个功能并不像听起来那么简单&#xff0c;因为Java语言实际上将堆栈跟踪暴露给程序员。考虑以下程序&#xff1a;public class Test {public static String f() {String s Math.random() > .5 ? f() : g();return s;}publ…

【AngularJS】—— 2 初识AngularJs(续)

前一篇了解了AngularJS的一些简单的使用&#xff0c;这里继续跟着w3c学习一下剩下的内容。 本篇根据w3cschool.cc继续学习AngularJS剩余的内容&#xff0c;包括&#xff1a; 1 事件 2 模块 3 表单 4 数据验证 5 bootstrap CSS风格 6 include包含其他页面 7 应用程序 8 参考手册…

特斯拉为何使用.NET 技术栈?

【精选转载】| 来源/知乎在知乎上有一个帖子非常热闹&#xff1a;“为何特使拉使用.net core技术栈 而不用 java&#xff1f;”1回答1&#xff1a;Kasim作者&#xff1a;Kasim链接&#xff1a;https://www.zhihu.com/question/496204534/answer/2269157872这题我熟啊&#xff0…

Blazor University (6)组件 — 组件事件

原文链接&#xff1a;https://blazor-university.com/components/component-events/组件事件源代码[1]EventCallback<T> 类是一个特殊的 Blazor 类&#xff0c;可以作为参数公开&#xff0c;以便组件可以在发生感兴趣的事情时轻松通知使用者。一旦声明了 EventCallback&l…

Xamarin效果第十四篇之玩耍GIS

最近再次拾起Xamarin然后也实现了祖传PLC控制和弹窗配置;这不又一次勾起来我想基于他玩玩原来一直玩耍的GIS,毕竟咱前面一直玩耍二维和三维的GIS相关的知识点;有兴趣的小伙伴可以翻翻我的历史文章;趁着激情满满;来看看最终咱实现的加载高德平面地图效果(有水印):再者就是满足群…

PHP进程退出信号_一文吃透 PHP 进程信号处理

背景前两周老大给安排了一个任务&#xff0c;写一个监听信号的包。因为我司的项目是运行在容器里边的&#xff0c;每次上线&#xff0c;需要重新打包镜像&#xff0c;然后启动。在重新打包之前&#xff0c;Dokcer会先给容器发送一个信号&#xff0c;然后等待一段超时时间(默认1…

GitHub Copilot 现已登陆 Visual Studio!

激动人心的好消息来了&#xff0c;GitHub 在3月29日发布博客&#xff0c;宣布 Github Copilot 现在可以在 Visual Studio 中使用。我们知道 Visual Studio 的 IntelliCode 本身已经很智能了, 现在又迎来了 Copilot, 编程体验将进入新的篇章。如何安装? 首先&#xff0c;您…

iOS 9音频应用播放音频之音量设置与声道设置

iOS 9音频应用播放音频之音量设置与声道设置 iOS 9音频应用音量设置 音量又称响度、音强&#xff0c;是指人耳对所听到的声音大小强弱的主观感受&#xff0c;其客观评价尺度是声音的振幅大小。在iOS 9音频应用的应用中&#xff0c;经常会出现播放的音乐音量过大或者过小。此时i…

php fpm工作原理,什么是phpfpm的工作原理?

什么是phpfpm的工作原理&#xff1f;发布时间&#xff1a;2020-07-13 15:12:53来源&#xff1a;亿速云阅读&#xff1a;181作者&#xff1a;Leah什么是phpfpm的工作原理&#xff1f;针对这个问题&#xff0c;这篇文章详细介绍了相对应的分析和解答&#xff0c;希望可以帮助更多…

C#对象映射器之Mapster

简介Mapster是一个快&#xff0c;小巧&#xff0c;功能强大的对象映射.Net框架例子我有两个Model类且他们的属性一致&#xff0c;我们将 SourceObjectTest赋值给DestObjectTest该怎么做&#xff1f;SourceObjectTest sourceObject new SourceObjectTest(); sourceObject.Name …

如何关闭Struts2的webconsole.html

出于安全目的&#xff0c;在禁用了devMode之后&#xff0c;仍然不希望其他人员看到webconsole.html页面&#xff0c;则可以直接删除webconsole.html 的源文件&#xff0c; 它的位置存在于&#xff1a; 我们手工删除 struts2-core-*.jar\org\apache\struts2\interceptor\debuggi…

UIView 的基础

UIView•什么是控件&#xff1f;-屏幕上的所有UI元素都叫做控件&#xff0c;也有人叫做视图、组件-按钮&#xff08;UIButton&#xff09;、文本&#xff08;UILabel&#xff09;都是控件•控件的共同属性有哪些&#xff1f;-尺寸-位置-背景色-......-•苹果将控件的共同属性都…

JS

为什么80%的码农都做不了架构师&#xff1f;>>> function getQueryString(name) {var reg new RegExp("(^|&)" name "([^&]*)(&|$)"),r window.location.search.substr(1).match(reg);if(r ! null) {return unescape(r[2]); }r…

ssh公钥免密码登录

2019独角兽企业重金招聘Python工程师标准>>> ssh 无密码登录要使用公钥与私钥。linux下可以用用ssh-keygen生成公钥/私钥对&#xff0c;下面我以CentOS为例。 有机器A(192.168.1.155)&#xff0c;B(192.168.1.181)。现想A通过ssh免密码登录到B。 首先以root账户登陆…

Spring4Shell的漏洞原理分析

Spring框架最新的PoC这两天出来的一个RCE漏洞&#xff0c;但是有以下的条件限制才行&#xff1a;必须是jdk9及以上必须是部署在tomcat的应用是springmvc的或者webflux的应用具体的可以查看spring官方&#xff1a;https://spring.io/blog/2022/03/31/spring-framework-rce-early…

php 点对点,浅析点对点(End-to-End)的场景文字识别

一、背景随着智能手机的广泛普及和移动互联网的迅速发展&#xff0c;通过手机等移动终端的摄像头获取、检索和分享资讯已经逐步成为一种生活方式。基于摄像头的(Camera-based)的应用更加强调对拍摄场景的理解。通常&#xff0c;在文字和其他物体并存的场景&#xff0c;用户往往…

【ArcGIS遇上Python】Python实现Modis NDVI批量化月最大合成

「 刘一哥GIS」CSDN专业技术博文专栏目录索引https://geostorm.blog.csdn.net/article/details/113732454 最大合成法(MVC)可以在Envi中的Band Math中进行,式子是B1>B2,但是无法批量化;本文实现在ArcGIS中利用Python代码批量进行,如下: 用到的Modis NDVI数据是在MRT…

cad2016中选择全图字体怎么操作_打开CAD图纸字体丢失、重新选择怎么办?这样设置,一辈子用的到...

AutoCAD图纸本身就有着比较特殊的个性&#xff0c;难编辑难打开&#xff0c;时不时的还会来个乱码、字体缺失&#xff0c;甚至有的时候还提示我们进行字体的重新选择&#xff0c;应该怎么解决呢&#xff1f;虽然是个很经常遇见的问题&#xff0c;很多的小伙伴还是不知道如何解决…

MassTransit - .NET Core 的分布式应用程序框架

简介MassTransit 是一个免费的、开源的.NET 分布式应用程序框架。MassTransit 使创建应用程序和服务变得容易&#xff0c;这些应用程序和服务利用基于消息的松散耦合异步通信来实现更高的可用性、可靠性和可扩展性特点•易于使用和理解的 API&#xff0c;让您专注于解决业务问题…

mongo学习笔记(二):聚合,游标

一、聚合 <1> Count 1.db.person.count() 2.db.person.count({"age":20}) <2> Distinct db.person.distinct("age")//指定了谁&#xff0c;谁就不能重复 <3> Group key&#xff1a;这个就是分组的key&#xff0c;我们这里是对年龄分组。…