首頁 後端開發 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:34 PM

C# 階乘指南。這裡我們討論 C# 中階乘的介紹以及不同的範例和程式碼實作。

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

C# 模式指南。在這裡,我們討論 C# 中模式的介紹和前 3 種類型,以及其範例和程式碼實作。

c#多線程和異步的區別 c#多線程和異步的區別 Apr 03, 2025 pm 02:57 PM

多線程和異步的區別在於,多線程同時執行多個線程,而異步在不阻塞當前線程的情況下執行操作。多線程用於計算密集型任務,而異步用於用戶交互操作。多線程的優勢是提高計算性能,異步的優勢是不阻塞 UI 線程。選擇多線程還是異步取決於任務性質:計算密集型任務使用多線程,與外部資源交互且需要保持 UI 響應的任務使用異步。

C# 中的質數 C# 中的質數 Sep 03, 2024 pm 03:35 PM

C# 質數指南。這裡我們討論c#中素數的介紹和範例以及程式碼實作。

See all articles