分析.Net裡執行緒同步機制
我們知道並行程式設計模型兩種:一種是基於訊息式的,第二種是基於共享記憶體式的。 前段時間專案中遇到了第二種 使用多執行緒開發並行程式共享資源的問題 ,今天以實際案例出發對.net
裡的共享記憶體式的執行緒同步機制做個總結,由於某些類庫的應用屬於基礎,所以本次不對基本使用做出講解,基本使用 MSDN是最好的教程。
一、volatile
關鍵字
基本介紹: 封裝了 Thread.VolatileWrite()
和 Thread.VolatileRead()
的實現 ,主要作用是強制重新整理快取記憶體。
使用場景: 適用於在多核多CPU的機器上 解決變數在記憶體和快取記憶體同步不及時的問題。
案例:參考下文 二、原子操作的 案例 或者 System.Collections.Concurrent
名稱空間下的 ConcurrentQueue
,ConcurrentDictionary
等併發集合的實現方式。
二、原子操作(Interlock
)
基本介紹: 原子操作是 實現Spinlock
,Monitor
,ReadWriterLock
鎖的基礎,其實現原理是在計算機匯流排上標誌一個訊號來表示資源已經被佔用 如果其他指令進行修改則等待本次操作完成後才能進行,因為原子操作是在硬體上實現的 所以速度非常快,大約在50個時鐘週期。其實原子操作也可以看做一種鎖。
使用場景:效能要求較高的場合,需要對欄位進行快速的同步或者對變數進行原子形式的跟新操作(例如:int b=0; b=b+1
實際分解為多條彙編指令,在多執行緒情況下 多條彙編指令並行的執行可能導致錯誤的結果,所以要保證執行 b=b+1
生成的彙編指令是一個原子形式執行 ),例如實現一個並行佇列,非同步佇列等。
案例:一個基於事件觸發機制佇列的實現
/// <summary>
/// 表示一個實時處理佇列
/// </summary>
public class ProcessQueue<T>
{
#region [成員]
private ConcurrentQueue<IEnumerable<T>> queue;
private Action<IEnumerable<T>> PublishHandler;
//指定處理的執行緒數
private int core = Environment.ProcessorCount;
//正在執行的執行緒數
private int runingCore = 0;
public event Action<Exception> OnException;
//佇列是否正在處理資料
private int isProcessing=0;
//佇列是否可用
private bool enabled = true;
#endregion
#region 建構函式
public ProcessQueue(Action<IEnumerable<T>> handler)
{
queue = new ConcurrentQueue<IEnumerable<T>>();
PublishHandler = handler;
this.OnException += ProcessException.OnProcessException;
}
#endregion
#region [方法]
/// <summary>
/// 入隊
/// </summary>
/// <param name="items">資料集合</param>
public void Enqueue(IEnumerable<T> items)
{
if (items != null)
{
queue.Enqueue(items);
}
//判斷是否佇列有執行緒正在處理
if (enabled && Interlocked.CompareExchange(ref isProcessing, 1, 0) == 0)
{
if (!queue.IsEmpty)
{
ThreadPool.QueueUserWorkItem(ProcessItemLoop);
}
else
{
Interlocked.Exchange(ref isProcessing, 0);
}
}
}
/// <summary>
/// 開啟佇列資料處理
/// </summary>
public void Start()
{
Thread process_Thread = new Thread(PorcessItem);
process_Thread.IsBackground = true;
process_Thread.Start();
}
/// <summary>
/// 迴圈處理資料項
/// </summary>
/// <param name="state"></param>
private void ProcessItemLoop(object state)
{
//表示一個執行緒遞迴 當處理完當前資料時 則開起執行緒處理佇列中下一條資料 遞迴終止條件是佇列為空時
//但是可能會出現 佇列有資料但是沒有執行緒去處理的情況 所有一個監視執行緒監視佇列中的資料是否為空,如果為空
//並且沒有執行緒去處理則開啟遞迴執行緒
if (!enabled && queue.IsEmpty)
{
Interlocked.Exchange(ref isProcessing, 0);
return;
}
//處理的執行緒數 是否小於當前CPU核數
if (Thread.VolatileRead(ref runingCore) <= core * 2*)
{
IEnumerable<T> publishFrame;
//出隊以後交給執行緒池處理
if (queue.TryDequeue(out publishFrame))
{
Interlocked.Increment(ref runingCore);
try
{
PublishHandler(publishFrame);
if (enabled && !queue.IsEmpty)
{
ThreadPool.QueueUserWorkItem(ProcessItemLoop);
}
else
{
Interlocked.Exchange(ref isProcessing, 0);
}
}
catch (Exception ex)
{
OnProcessException(ex);
}
finally
{
Interlocked.Decrement(ref runingCore);
}
}
}
}
/// <summary>
///定時處理幀 執行緒呼叫函式
///主要是監視入隊的時候執行緒 沒有來的及處理的情況
/// </summary>
private void PorcessItem(object state)
{
int sleepCount=0;
int sleepTime = 1000;
while (enabled)
{
//如果佇列為空則根據迴圈的次數確定睡眠的時間
if (queue.IsEmpty)
{
if (sleepCount == 0)
{
sleepTime = 1000;
}
else if (sleepCount == 3)
{
sleepTime = 1000 * 3;
}
else if (sleepCount == 5)
{
sleepTime = 1000 * 5;
}
else if (sleepCount == 8)
{
sleepTime = 1000 * 8;
}
else if (sleepCount == 10)
{
sleepTime = 1000 * 10;
}
else
{
sleepTime = 1000 * 50;
}
sleepCount++;
Thread.Sleep(sleepTime);
}
else
{
//判斷是否佇列有執行緒正在處理
if (enabled && Interlocked.CompareExchange(ref isProcessing, 1, 0) == 0)
{
if (!queue.IsEmpty)
{
ThreadPool.QueueUserWorkItem(ProcessItemLoop);
}
else
{
Interlocked.Exchange(ref isProcessing, 0);
}
sleepCount = 0;
sleepTime = 1000;
}
}
}
}
/// <summary>
/// 停止佇列
/// </summary>
public void Stop()
{
this.enabled = false;
}
/// <summary>
/// 觸發異常處理事件
/// </summary>
/// <param name="ex">異常</param>
private void OnProcessException(Exception ex)
{
var tempException = OnException;
Interlocked.CompareExchange(ref tempException, null, null);
if (tempException != null)
{
OnException(ex);
}
}
#endregion
}
三、自旋鎖(Spinlock
)
基本介紹: 在原子操作基礎上實現的鎖,使用者態的鎖,缺點是執行緒一直不釋放CPU時間片。作業系統進行一次執行緒使用者態到核心態的切換大約需要500個時鐘週期,可以根據這個進行參考我們的執行緒是進行使用者等待還是轉到核心的等待.。
使用場景:執行緒等待資源時間較短的情況下使用。
案例: 和最常用的Monitor
使用方法一樣 這裡就不舉例了,在實際場景中應該優先選擇使用Monitor
,除非是執行緒等待資源的時間特別的短。
四、監視器(Monitor
)
基本介紹: 原子操作基礎上實現的鎖,開始處於使用者態,自旋一段時間進入核心態的等待釋放CPU時間片,缺點使用不當容易造成死鎖 c#實現的關鍵字是Lock
。
使用場景: 所有需要加鎖的場景都可以使用。
案例: 案例太多了,這裡就不列出了。
五、讀寫鎖(ReadWriterLock
)
原理分析: 原子操作基礎上實現的鎖,
使用場景:適用於寫的次數少,讀的頻率高的情況。
案例:一個執行緒安全的快取實現(.net 4.0
可以使用基礎類庫中的 ConcurrentDictionary<K,V>
) 注意:老版本ReaderWriterLock
已經被淘汰,新版的是ReaderWriterLockSlim
class CacheManager<K, V>
{
#region [成員]
private ReaderWriterLockSlim readerWriterLockSlim;
private Dictionary<K, V> containter;
#endregion
#region [建構函式]
public CacheManager()
{
this.readerWriterLockSlim = new ReaderWriterLockSlim();
this.containter = new Dictionary<K, V>();
}
#endregion
#region [方法]
public void Add(K key, V value)
{
readerWriterLockSlim.EnterWriteLock();
try
{
containter.Add(key, value);
}
finally
{
readerWriterLockSlim.ExitWriteLock();
}
}
public V Get(K key)
{
bool result = false;
V value;
do
{
readerWriterLockSlim.EnterReadLock();
try
{
result = containter.TryGetValue(key, out value);
}
finally
{
readerWriterLockSlim.ExitWriteLock();
}
} while (!result);
return value;
}
#endregion
}
.net
中還有其他的執行緒同步機制:ManualResetEventSlim
,AutoResetEvent
,SemaphoreSlim
這裡就逐個進行不介紹 具體在《CLR Via C# 》
中解釋的非常詳細,但在具體的實際開發中我還沒有使用到。
最好的執行緒同步機制是沒有同步,這取決於良好的設計,當然有些情況下無法避免使用鎖。 在效能要求不高的場合基本的lock
就能滿足要求,但效能要求比較苛刻的情就需求更具實際場景進行選擇哪種執行緒同步機制。
相關文章
- 執行緒同步機制執行緒
- java synchronize - 執行緒同步機制Java執行緒
- 執行緒同步機制-包裝類執行緒
- 深入理解 OpenMP 執行緒同步機制執行緒
- ConcurrentHashMap執行緒安全機制以及原始碼分析HashMap執行緒原始碼
- .NET中各種執行緒同步鎖執行緒
- 一起分析執行緒的狀態及執行緒通訊機制執行緒
- JS執行機制--同步與非同步JS非同步
- JavaScript執行緒機制與事件機制JavaScript執行緒事件
- 執行緒鎖 -賣票機制執行緒
- 多執行緒和多執行緒同步執行緒
- Python並行程式設計(二):多執行緒鎖機制利用Lock與RLock實現執行緒同步Python並行行程程式設計執行緒
- Java的執行機制分析!Java
- 執行緒與同步非同步執行緒非同步
- 執行緒同步方法執行緒
- 深入執行緒同步執行緒
- 執行緒的同步執行緒
- 理解執行緒同步執行緒
- 【多執行緒總結(二)-執行緒安全與執行緒同步】執行緒
- JAVA多執行緒與鎖機制Java執行緒
- 多執行緒之等待通知機制執行緒
- 執行緒間的協作機制執行緒
- FlutterEngine執行緒管理與DartIsolate機制Flutter執行緒Dart
- .net使用Task多執行緒執行任務 .net限制執行緒數量執行緒
- 多執行緒(2)-執行緒同步互斥鎖Mutex執行緒Mutex
- 非同步/同步,阻塞/非阻塞,單執行緒/多執行緒概念梳理非同步執行緒
- 建立程序,設計訊號量同步機制,實現多執行緒同步 - C語言版執行緒C語言
- java 多執行緒 –同步Java執行緒
- java 多執行緒 --同步Java執行緒
- 執行緒間通訊_等待/通知機制執行緒
- Java多執行緒學習(3)執行緒同步與執行緒通訊Java執行緒
- 多執行緒(2)-執行緒同步條件變數執行緒變數
- Java多執行緒之執行緒同步【synchronized、Lock、volatitle】Java執行緒synchronized
- C#多執行緒開發-執行緒同步 02C#執行緒
- .NET非同步程式設計——給執行緒傳遞資料非同步程式設計執行緒
- Java 執行緒間通訊 —— 等待 / 通知機制Java執行緒
- 簡單案例淺析JS執行緒機制JS執行緒
- js內部事件機制–單執行緒原理JS事件執行緒