首页 后端开发 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

热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

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

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

使用 C# 的活动目录 使用 C# 的活动目录 Sep 03, 2024 pm 03:33 PM

使用 C# 的 Active Directory 指南。在这里,我们讨论 Active Directory 在 C# 中的介绍和工作原理以及语法和示例。

C# 序列化 C# 序列化 Sep 03, 2024 pm 03:30 PM

C# 序列化指南。这里我们分别讨论C#序列化对象的介绍、步骤、工作原理和示例。

C# 中的随机数生成器 C# 中的随机数生成器 Sep 03, 2024 pm 03:34 PM

C# 随机数生成器指南。在这里,我们讨论随机数生成器的工作原理、伪随机数和安全数的概念。

C# 数据网格视图 C# 数据网格视图 Sep 03, 2024 pm 03:32 PM

C# 数据网格视图指南。在这里,我们讨论如何从 SQL 数据库或 Excel 文件加载和导出数据网格视图的示例。

C# 中的模式 C# 中的模式 Sep 03, 2024 pm 03:33 PM

C# 模式指南。在这里,我们讨论 C# 中模式的介绍和前 3 种类型,以及其示例和代码实现。

C# 中的阶乘 C# 中的阶乘 Sep 03, 2024 pm 03:34 PM

C# 阶乘指南。这里我们讨论 C# 中阶乘的介绍以及不同的示例和代码实现。

C# 中的质数 C# 中的质数 Sep 03, 2024 pm 03:35 PM

C# 素数指南。这里我们讨论c#中素数的介绍和示例以及代码实现。

c#多线程和异步的区别 c#多线程和异步的区别 Apr 03, 2025 pm 02:57 PM

多线程和异步的区别在于,多线程同时执行多个线程,而异步在不阻塞当前线程的情况下执行操作。多线程用于计算密集型任务,而异步用于用户交互操作。多线程的优势是提高计算性能,异步的优势是不阻塞 UI 线程。选择多线程还是异步取决于任务性质:计算密集型任务使用多线程,与外部资源交互且需要保持 UI 响应的任务使用异步。

See all articles