C# 線程同步的方法
一、進(jìn)程內(nèi)部的線程同步
1、使用lock,用法如下:
private static readonly object SeqLock = new object();
private void Print()
{
lock (SeqLock)
{
Console.WriteLine("test");
}
}
特性:只能傳遞對(duì)象,無法設(shè)置等待超時(shí)
2、使用:InterLocked(原子操作)
其在System.Threading命名空間下,Interlocked實(shí)際是類控制計(jì)數(shù)器,從而實(shí)現(xiàn)進(jìn)程的同步,其很容易實(shí)現(xiàn)生產(chǎn)者消費(fèi)者模型
//緩沖區(qū),只能容納一個(gè)字符
private static char buffer;
//標(biāo)識(shí)量(緩沖區(qū)中已使用的空間,初始值為0)
private static long numberOfUsedSpace = 0;
static void Main(string[] args)
{
//線程:寫入者
Thread Writer = new Thread(delegate ()
{
string str = "這里面的字會(huì)一個(gè)一個(gè)讀取出來,一個(gè)都不會(huì)少,,,";
for (int i = 0; i < 24; i++)
{
//寫入數(shù)據(jù)前檢查緩沖區(qū)是否已滿
//如果已滿,就進(jìn)行等待,直到緩沖區(qū)中的數(shù)據(jù)被進(jìn)程Reader讀取為止
while (Interlocked.Read(ref numberOfUsedSpace) == 1)
{
Thread.Sleep(50);
}
buffer = str[i]; //向緩沖區(qū)寫入數(shù)據(jù)
//寫入數(shù)據(jù)后把緩沖區(qū)標(biāo)記為滿(由0變?yōu)?)
Interlocked.Increment(ref numberOfUsedSpace);
}
});
//線程:讀出者
Thread Reader = new Thread(delegate ()
{
for (int i = 0; i < 24; i++)
{
//讀取數(shù)據(jù)前檢查緩沖區(qū)是否為空
//如果為空,就進(jìn)行等待,直到進(jìn)程Writer向緩沖區(qū)中寫入數(shù)據(jù)為止
while (Interlocked.Read(ref numberOfUsedSpace) == 0)
{
Thread.Sleep(50);
}
char ch = buffer; //從緩沖區(qū)讀取數(shù)據(jù)
Console.Write(ch);
Interlocked.Decrement(ref numberOfUsedSpace);
}
});
//啟動(dòng)線程
Writer.Start();
Reader.Start();
Console.ReadKey();
3、使用Monitor
其中Monitor.Enter()和lock相同
Monitor.Enter(obj){
//Synchronized part
}finally{
Monitor.Exit(obj);
}
TryEnter則可設(shè)置等待時(shí)間等
bool lockTaken=false;
Monitor.TryEnter(obj, 500, ref lockTaken);
if(lockTaken){
try
{
//Synchronized part
}
finally
{
Monitor.Exit(obj);
}
}else{
//don't aquire the lock, excute other parts
}
二、進(jìn)程間的同步
1. WaitHandle:
封裝等待對(duì)共享資源進(jìn)行獨(dú)占訪問的操作系統(tǒng)特定的對(duì)象。 WaitHandle:是一個(gè)抽象類,我們一般不直接用,而是用它的派生類:
AutoResetEvent、EventWaitHandle、ManualResetEvent、Mutex、Semaphore
這個(gè)抽象類的方法如下:
WaitOne(): 等待一個(gè)信號(hào)的出現(xiàn),可設(shè)置超時(shí);
WaitAll(): 等待多個(gè)信號(hào)的出現(xiàn),可設(shè)置超時(shí);
WaitAny(): 等待任意一個(gè)信號(hào)的出現(xiàn),可設(shè)置超時(shí);
2、Mutex: 與Monitor 類似,只有一個(gè)線程能夠獲取鎖定。利用WaitOne() 獲取鎖定,利用ReleaseMutex() 解除鎖定。構(gòu)造函數(shù)使用如下:
bool isNew = false;
mutex = new Mutex(false, "Mutex1", out isNew);
參數(shù)1:鎖創(chuàng)建后是否由主調(diào)線程擁有。 如果設(shè)為true,相當(dāng)于調(diào)用了WaitOne(),需要釋放,否則其他線程無法獲取鎖;
參數(shù)2:鎖名稱,可通過OpenExist()或TryOpenExist() 打開已有鎖,因?yàn)椴僮飨到y(tǒng)識(shí)別有名稱的互鎖,所以可由不同的進(jìn)程共享。若鎖名稱為空,就是未命名的互鎖,不能在多個(gè)進(jìn)程之間共享;
參數(shù)3: 是否為新創(chuàng)建的互鎖;
下面的例子演示Mutex 在進(jìn)程之間的使用: class Program
private static Mutex mutex = null;
static void Main(string[] args)
{
bool isNew = false;
mutex = new Mutex(false, "Mutex1", out isNew);
Console.WriteLine("Main Start....");
mutex.WaitOne();
Console.WriteLine("Aquire Lock and Running....");
Thread.Sleep(10000);
mutex.ReleaseMutex();
Console.WriteLine("Release Lock....");
Console.WriteLine("Main end....");
Console.ReadLine();
}
}
連續(xù)2次運(yùn)行這個(gè)控制臺(tái)程序的exe,結(jié)果如下,首先運(yùn)行的獲取 Mutex1 互鎖, 后面運(yùn)行的會(huì)等待直到前面運(yùn)行的釋放 Mutex1 互鎖。
3.Semaphore: 信號(hào)量的作用于互斥鎖類似,但它可以定義一定數(shù)量的線程同時(shí)使用。下面是構(gòu)造函數(shù):
bool isNew = false;
semaphore = new Semaphore(3, 3, "semaphore1", out isNew);
參數(shù)1:創(chuàng)建后,最初釋放的鎖的數(shù)量,如參數(shù)1設(shè)為2,參數(shù)2設(shè)為3,則創(chuàng)建后只有2個(gè)鎖可用,另1個(gè)已經(jīng)鎖定;
參數(shù)2:定義可用鎖的數(shù)量;
參數(shù)3: 信號(hào)量的名稱,與Mutex類似;
參數(shù)4:是否為新創(chuàng)建的互鎖;
以下例子創(chuàng)建了信號(hào)量“semaphore1”,利用Parallel.For() 同步運(yùn)行Func1() ,在Func1() 中,當(dāng)線程獲取信號(hào)量鎖,釋放鎖或等待超時(shí),都會(huì)在控制臺(tái)里輸出,
class Program
{
private static Semaphore semaphore = null;
static void Main(string[] args)
{
Console.WriteLine("Main Start....");
bool isNew = false;
semaphore = new Semaphore(3, 3, "semaphore1", out isNew);
Parallel.For(0, 6, Func1);
Console.WriteLine("Main end....");
Console.ReadLine();
}
static void Func1(int index)
{
Console.WriteLine("Task {0} Start....",Task.CurrentId);
bool isComplete = false;
while (!isComplete)
{
if (semaphore.WaitOne(1000))
{
try
{
Console.WriteLine("Task {0} aquire lock....", Task.CurrentId);
Thread.Sleep(5000);
}
finally
{
semaphore.Release();
Console.WriteLine("Task {0} release lock....", Task.CurrentId);
isComplete = true;
}
}
else
{
Console.WriteLine("Task {0} timeout....", Task.CurrentId);
}
}
}
運(yùn)行結(jié)果如下,線程1,2,3首先獲取信號(hào)量鎖,線程4,5,6在等待,直到1,2,3釋放,
4. AutoResetEvent 類:
可以使用事件通知其他任務(wù),構(gòu)造函數(shù)為 public AutoResetEvent(bool initialState)。
當(dāng)initialState=true,處于signaled 模式(終止?fàn)顟B(tài)),調(diào)用waitone() 也不會(huì)阻塞任務(wù),等待信號(hào),調(diào)用Reset()方法,可以設(shè)置為non-signaled 模式;
當(dāng)initialState=fasle,處于non-signaled 模式(非終止?fàn)顟B(tài)),調(diào)用waitone() 會(huì)等待信號(hào)阻塞當(dāng)前線程(可以在多個(gè)線程中調(diào)用,同時(shí)阻塞多個(gè)線程),直到調(diào)用set()發(fā)送信號(hào)釋放線程(調(diào)用一次,只能釋放一個(gè)線程),一般使用這種方式;
以下例子創(chuàng)建5個(gè)任務(wù),分別調(diào)用waitone()阻塞線程,接著每隔2s 調(diào)用set(),
private static AutoResetEvent autoReset = new AutoResetEvent(false);
static void Main(string[] args)
{
Console.WriteLine("Main Start....");
for (int i = 0; i < 5; i++)
{
Task.Factory.StartNew(() =>
{
Console.WriteLine("{0} Start....", Task.CurrentId);
autoReset.WaitOne();
Console.WriteLine("{0} Continue....", Task.CurrentId);
});
}
for (int i = 0; i < 5;i++ )
{
Thread.Sleep(2000);
autoReset.Set();
}
Console.WriteLine("Main end....");
Console.ReadLine();
}
運(yùn)行結(jié)果每次順序略有不同,釋放是隨機(jī)的:
5. ManualResetEvent 類:功能基本上和AutoSetEvent類似,但又一個(gè)不同點(diǎn):
使用AutoSetEvent,每次調(diào)用set(),切換到終止模式,只能釋放一個(gè)waitone(),便會(huì)自動(dòng)切換到非終止模式;但ManualResetEvent,調(diào)用set(),切換到終止模式,可以釋放當(dāng)前所有的waitone(),需要手動(dòng)調(diào)用reset()才能切換到非終止模式。
以下例子說明了這個(gè)不同的:
private static ManualResetEvent manualReset = new ManualResetEvent(false);
static void Main(string[] args)
{
Console.WriteLine("Main Start....");
for (int i = 0; i < 5; i++)
{
Task.Factory.StartNew(() =>
{
Console.WriteLine("{0} Start....", Task.CurrentId);
manualReset.WaitOne();
Console.WriteLine("{0} Continue....", Task.CurrentId);
});
}
Thread.Sleep(2000);
manualReset.Set();
manualReset.WaitOne();
Console.WriteLine("it doesn't work now, Main continue....");
manualReset.Reset();
manualReset.WaitOne();
Console.WriteLine("Main end....");
Console.ReadLine();
}
以上就是C# 線程同步的方法的詳細(xì)內(nèi)容,更多關(guān)于c# 線程同步的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
C#Process的OutputDataReceived事件不觸發(fā)問題及解決
這篇文章主要介紹了C#Process的OutputDataReceived事件不觸發(fā)問題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-02-02
C#中隱藏TabControl選項(xiàng)卡標(biāo)簽的解決方案
這篇文章主要介紹了C#中隱藏TabControl選項(xiàng)卡標(biāo)簽的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-04-04
C#?中?List?與?List?多層嵌套不改變?cè)档膶?shí)現(xiàn)方法(深度復(fù)制)
這篇文章主要介紹了C#?中?List?與?List?多層嵌套不改變?cè)档膶?shí)現(xiàn)方法,使用?BinaryFormatter?將原始?List?序列化為字節(jié)流,然后再反序列化得到新的?List,實(shí)現(xiàn)了深度復(fù)制,需要的朋友可以參考下2024-03-03
C# MVC模式中應(yīng)該怎樣區(qū)分應(yīng)用程序邏輯(Controller層)和業(yè)務(wù)邏輯(Model層)?
這篇文章主要介紹了C# MVC模式中應(yīng)該怎樣區(qū)分應(yīng)用程序邏輯(Controller層)和業(yè)務(wù)邏輯(Model層)?,這也小編做.NET項(xiàng)目時(shí)經(jīng)常思考和讓人混亂的一個(gè)問題,這篇文章寫的挺好,一下清晰了許多,需要的朋友可以參考下2015-06-06
C#的FileSystemWatcher用法實(shí)例詳解
這篇文章主要介紹了C#的FileSystemWatcher用法,以實(shí)例形似詳細(xì)分析了FileSystemWatcher控件主要功能,并總結(jié)了FileSystemWatcher控件使用的技巧,需要的朋友可以參考下2014-11-11
WPF實(shí)現(xiàn)動(dòng)畫效果(五)之關(guān)鍵幀動(dòng)畫
這篇文章介紹了WPF實(shí)現(xiàn)動(dòng)畫效果之關(guān)鍵幀動(dòng)畫,對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-06-06

