旗下导航:搜·么
当前位置:网站首页 > .Net教程 > 正文

C# 线程同步的图文代码实例细致引见【C#.Net教程】,C#,线程同步

作者:搜教程发布时间:2019-11-27分类:.Net教程浏览:22评论:0


导读:本文重要引见了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# 线程同步的图文代码实例细致引见的细致内容,更多请关注ki4网别的相干文章!

标签:C#线程同步


欢迎 发表评论: