搜索
首页后端开发C#.Net教程C# 线程同步的图文代码实例详细介绍

C# 线程同步的图文代码实例详细介绍

Mar 08, 2017 am 11:14 AM
c#线程同步

本文主要介绍了C#中线程同步的相关知识。具有很好的参考价值,下面跟着小编一起来看下吧

前言

当线程池的线程阻塞时,线程池会创建额外的线程,而创建、销毁和调度线程所需要相当昂贵的内存资源,另外,很多的开发人员看见自己程序的线程没有做任何有用的事情时习惯创建更多的线程,为了构建可伸缩、响应灵敏的程序,我们在前面介绍了C#异步编程详解

但是异步编程同样也存在着很严重的问题,如果两个不同的线程访问相同的变量和数据,按照我们异步函数的实现方式,不可能存在两个线程同时访问相同的数据,这个时候我们就需要线程同步。多个线程同时访问共享数据的时,线程同步能防止数据损坏,之所以强调同时这个概念,因为线程同步本质就是计时问题。

异步和同步是相对的,同步就是顺序执行,执行完一个再执行下一个,需要等待、协调运行。异步就是彼此独立,在等待某事件的过程中继续做自己的事,不需要等待这一事件完成后再工作。线程就是实现异步的一个方式。异步是让调用方法的主线程不需要同步等待另一线程的完成,从而可以让主线程干其它的事情。

基元用户模式和内核模式构造

基础概念

基元:可以在代码中使用的简单的构造

用户模式:通过特殊的CPU指令协调线程,操作系统永远检测不到一个线程在基元用户模式的构造上阻塞。

内核模式:由windows自身提供,在应用程序的线程中调用由内核实现的函数。

用户模式构造

易变构造

C#编译器、JIT编译器和CPU都会对代码进行优化,它们尽量保证保留我们的意图,但是从多线程的角度出发,我们的意图并不一定会得到保留,下面举例说明:

 static void Main(string[] args)
 {
 Console.WriteLine("让worker函数运行5s后停止");
 var t = new Thread(Worker);
 t.Start();
 Thread.Sleep(5000);
 stop = true;
 Console.ReadLine();
 }
 private static bool stop = false;
 private static void Worker(object obj)
 {
 int x = 0;
 while (!stop)
 {
 x++;
 }
 Console.WriteLine("worker函数停止x={0}",x);
 }

 

编译器如果检查到stop为false,就生成代码来进入一个无限循环,并在循环中一直递增x,所以优化循环很快完成,但是编译器只检测stop一次,并不是每次都会检测。

例子2---两个线程同时访问:

class test
 {
 private static int m_flag = 0;
 private static int m_value = 0;
 public static void Thread1(object obj)
 {
 m_value = 5;
 m_flag = 1;
 }
 public static void Thread2(object obj)
 {
 if (m_flag == 1)
 Console.WriteLine("m_value = {0}", m_value);
 }
 //多核CPU机器才会出现线程同步问题
 public void Exec()
 {
 var thread1 = new Thread(Thread1);
 var thread2 = new Thread(Thread2);
 thread1.Start();
 thread2.Start();
 Console.ReadLine();
 }
 }

程序在执行的时候,编译器必须将变量m_flag和m_value从RAM读入CPU寄存器,RAM先传递m_value的值0,thread1把值变为5,但是thread2并不知道thread2仍然认为值为0,这种问题一般来说发生在多核CPU的概率大一些,应该CPU越多,多个线程同时访问资源的几率就越大。

关键字volatile,作用禁止C#编译器、JTP编译器和CPU执行的一些优化,如果做用于变量后,将不允许字段缓存到CPU的寄存器中,确保字段的读写都在RAM中进行。

互锁构造

System.Threading.Interlocked类中的每个方法都执行一次原子的读取以及写入操作,调用某个Interlocked方法之前的任何变量写入都在这个Interlocked方法调用之前执行,而调用之后的任何变量读取都在这个调用之后读取。

Interlocked方法主要是对INT32变量进行静态操作Add、Decrement、Compare、Exchange、CompareChange等方法,也接受object、Double等类型的参数。

原子操作:是指不会被线程调度机制打断的操作;这种操作一旦开始,就一直运行到结束,中间不会有任何 context switch (切换到另一个线程)。

代码演示:

说明:通过Interlocked的方法异步查询几个web服务器,并同时返回数据,且结果只执行一次。

//上报状态类型
 enum CoordinationStatus
 {
 Cancel,
 Timeout,
 AllDone
 }

class AsyncCoordinator
 {
 //AllBegun 内部调用JustEnded来递减它
 private int _mOpCount = 1;
 //0=false,1=true
 private int _mStatusReported = 0;
 private Action<CoordinationStatus> _mCallback;
 private Timer _mTimer;
 //发起一个操作之前调用
 public void AboutToBegin(int opsToAdd = 1)
 {
 Interlocked.Add(ref _mOpCount, opsToAdd);
 }
 //处理好一个操作的结果之后调用
 public void JustEnded()
 {
 if (Interlocked.Decrement(ref _mOpCount) == 0)
 {
 ReportStatus(CoordinationStatus.AllDone);
 } 
 }
 //该方法必须在发起所有操作后调用
 public void AllBegin(Action<CoordinationStatus> callback, int timeout = Timeout.Infinite)
 {
 _mCallback = callback;
 if (timeout != Timeout.Infinite)
 {
 _mTimer = new Timer(TimeExpired, null, timeout, Timeout.Infinite);
 JustEnded();
 }
 }
 private void TimeExpired(object o)
 {
 ReportStatus(CoordinationStatus.Timeout);
 }
 public void Cancel()
 {
 ReportStatus(CoordinationStatus.Cancel);
 }
 private void ReportStatus(CoordinationStatus status)
 {
 //如果状态从未报告过,就报告它,否则就忽略它,只调用一次
 if (Interlocked.Exchange(ref _mStatusReported, 1) == 0)
 {
 _mCallback(status);
 } 
 }
 }

class MultiWebRequest
 {
 //辅助类 用于协调所有的异步操作
 private AsyncCoordinator _mac = new AsyncCoordinator();
 protected Dictionary<string,object> _mServers = new Dictionary<string, object>
 {
 {"http://www.baidu.com",null},{"http://www.Microsoft.com",null},{"http://www.cctv.com",null},
 {"http://www.souhu.com",null},{"http://www.sina.com",null},{"http://www.tencent.com",null},
 {"http://www.youku.com",null}
 };
 private Stopwatch sp;
 public MultiWebRequest(int timeout = Timeout.Infinite)
 {
 sp = new Stopwatch();
 sp.Start();
 //通过异步方式一次性发起请求
 var httpclient = new HttpClient();
 foreach (var server in _mServers.Keys)
 {
 _mac.AboutToBegin(1);
 httpclient.GetByteArrayAsync(server).ContinueWith(task => ComputeResult(server, task));
 }
 _mac.AllBegin(AllDone,timeout);
 Console.WriteLine("");
 }
 private void ComputeResult(string server, Task<Byte[]> task)
 {
 object result;
 if (task.Exception != null)
 {
 result = task.Exception.InnerException;
 }
 else
 {
 //线程池处理IO
 result = task.Result.Length;
 }
 //保存返回结果的长度
 _mServers[server] = result;
 _mac.JustEnded();
 }
 public void Cancel()
 {
 _mac.Cancel();
 }
 private void AllDone(CoordinationStatus status)
 {
 sp.Stop();
 Console.WriteLine("响应耗时总计{0}",sp.Elapsed);
 switch (status)
 {
 case CoordinationStatus.Cancel:
  Console.WriteLine("操作取消");
  break;
 case CoordinationStatus.AllDone:
  Console.WriteLine("操作完成,完成的结果如下");
  foreach (var server in _mServers)
  {
  Console.WriteLine("{0}",server.Key);
  object result = server.Value;
  if (result is Exception)
  {
  Console.WriteLine("错误原因{0}",result.GetType().Name);
  }
  else
  {
  Console.WriteLine("返回字节数为:{0}",result);
  }
  }
  break;
 case CoordinationStatus.Timeout:
  Console.WriteLine("操作超时");
  break;
 default:
  throw new ArgumentOutOfRangeException("status", status, null);
 }
 }
 }

非常建议大家参考一下以上代码,我在对服务器进行访问时,也会常常参考这个模型。

简单的自旋锁

class SomeResource
 {
 private SimpleSpinLock s1 = new SimpleSpinLock();
 public void AccessResource()
 {
 s1.Enter();
 //一次是有一个线程才能进入访问
 s1.Leave();
 }
 }
 class SimpleSpinLock
 {
 private int _mResourceInUse;
 public void Enter()
 {
 while (true)
 {
 if(Interlocked.Exchange(ref _mResourceInUse,1)==0)
  return;
 }
 }
 public void Leave()
 {
 Volatile.Write(ref _mResourceInUse,1);
 }
 }

这就是一个线程同步锁的简单实现,这种锁的最大问题在于,存在竞争的情况下会造成线程的“自旋”,这会浪费CPU的宝贵时间,组织CPU做更多的工作,因此,这种自旋锁应该用于保护那些执行的非常快的代码。


以上是C# 线程同步的图文代码实例详细介绍的详细内容。更多信息请关注PHP中文网其他相关文章!

声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
使用C#.NET开发:实用指南和示例使用C#.NET开发:实用指南和示例May 12, 2025 am 12:16 AM

C#和.NET提供了强大的功能和高效的开发环境。1)C#是一种现代、面向对象的编程语言,结合了C 的强大和Java的简洁性。2).NET框架是一个用于构建和运行应用程序的平台,支持多种编程语言。3)C#中的类和对象是面向对象编程的核心,类定义数据和行为,对象是类的实例。4).NET的垃圾回收机制自动管理内存,简化开发者的工作。5)C#和.NET提供了强大的文件操作功能,支持同步和异步编程。6)常见错误可以通过调试器、日志记录和异常处理来解决。7)性能优化和最佳实践包括使用StringBuild

C#.NET:了解Microsoft .NET框架C#.NET:了解Microsoft .NET框架May 11, 2025 am 12:17 AM

.NETFramework是一个跨语言、跨平台的开发平台,提供一致的编程模型和强大的运行时环境。1)它由CLR和FCL组成,CLR管理内存和线程,FCL提供预构建功能。2)使用示例包括读取文件和LINQ查询。3)常见错误涉及未处理异常和内存泄漏,需使用调试工具解决。4)性能优化可通过异步编程和缓存实现,保持代码可读性和可维护性是关键。

c#.net的寿命:其持久流行的原因c#.net的寿命:其持久流行的原因May 10, 2025 am 12:12 AM

C#.NET保持持久吸引力的原因包括其出色的性能、丰富的生态系统、强大的社区支持和跨平台开发能力。1)性能表现优异,适用于企业级应用和游戏开发;2).NET框架提供了广泛的类库和工具,支持多种开发领域;3)拥有活跃的开发者社区和丰富的学习资源;4).NETCore实现了跨平台开发,扩展了应用场景。

掌握C#.NET设计模式:从单胎到依赖注入掌握C#.NET设计模式:从单胎到依赖注入May 09, 2025 am 12:15 AM

C#.NET中的设计模式包括Singleton模式和依赖注入。1.Singleton模式确保类只有一个实例,适用于需要全局访问点的场景,但需注意线程安全和滥用问题。2.依赖注入通过注入依赖提高代码灵活性和可测试性,常用于构造函数注入,但需避免过度使用导致复杂度增加。

现代世界中的C#.NET:应用和行业现代世界中的C#.NET:应用和行业May 08, 2025 am 12:08 AM

C#.NET在现代世界中广泛应用于游戏开发、金融服务、物联网和云计算等领域。1)在游戏开发中,通过Unity引擎使用C#进行编程。2)金融服务领域,C#.NET用于开发高性能的交易系统和数据分析工具。3)物联网和云计算方面,C#.NET通过Azure服务提供支持,开发设备控制逻辑和数据处理。

C#.NET开发人员社区:资源和支持C#.NET开发人员社区:资源和支持May 06, 2025 am 12:11 AM

C#.NET开发者社区提供了丰富的资源和支持,包括:1.微软的官方文档,2.社区论坛如StackOverflow和Reddit,3.GitHub上的开源项目,这些资源帮助开发者从基础学习到高级应用,提升编程技能。

C#.NET优势:功能,好处和用例C#.NET优势:功能,好处和用例May 05, 2025 am 12:01 AM

C#.NET的优势包括:1)语言特性,如异步编程简化了开发;2)性能与可靠性,通过JIT编译和垃圾回收机制提升效率;3)跨平台支持,.NETCore扩展了应用场景;4)实际应用广泛,从Web到桌面和游戏开发都有出色表现。

See all articles

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

Video Face Swap

Video Face Swap

使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热门文章

热工具

EditPlus 中文破解版

EditPlus 中文破解版

体积小,语法高亮,不支持代码提示功能

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

Atom编辑器mac版下载

Atom编辑器mac版下载

最流行的的开源编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

适用于 Eclipse 的 SAP NetWeaver 服务器适配器

适用于 Eclipse 的 SAP NetWeaver 服务器适配器

将Eclipse与SAP NetWeaver应用服务器集成。