C#多線程同步不同實現(xiàn)方式小結(jié)
AutoResetEvent
class MainClass
{
// the array of consumer threads
private static List<Thread> consumers = new List<Thread> ();
// the task queue
private static Queue<Action> tasks = new Queue<Action>();
// the synchronisation object for locking the task queue
private static readonly object queueLock = new object();
// this wait handle notifies consumers of a new task
private static EventWaitHandle newTaskAvailable = new AutoResetEvent (false);
// the synchronisation object for locking the console color
private static readonly object consoleLock = new object();
// enqueue a new task
private static void EnqueueTask (Action task)
{
lock (queueLock)
{
tasks.Enqueue (task);
}
newTaskAvailable.Set();
}
// thread work method for consumers
private static void DoWork(ConsoleColor color)
{
while (true)
{
// get a new task
Action task = null;
lock (queueLock) {
if (tasks.Count > 0)
{
task = tasks.Dequeue ();
}
}
if (task != null)
{
// set console to this task's color
lock (consoleLock)
{
Console.ForegroundColor = color;
}
// execute task
task ();
}
else
// queue is empty - wait for a new task
newTaskAvailable.WaitOne();
}
}
public static void Main (string[] args)
{
// set up 3 consumers
consumers.Add(new Thread ( () => { DoWork(ConsoleColor.Red); }));
consumers.Add(new Thread ( () => { DoWork(ConsoleColor.Green); }));
consumers.Add(new Thread ( () => { DoWork(ConsoleColor.Blue); }));
// start all consumers
consumers.ForEach ( (t) => { t.Start (); });
while (true)
{
// add a new task
Random r = new Random();
EnqueueTask ( () => {
// the task is to write a random number to the console
int number = r.Next (10);
Console.Write (number);
});
// random sleep to simulate workload
Thread.Sleep (r.Next (1000));
}
}
}
這段代碼實現(xiàn)了一個簡單的生產(chǎn)者-消費者模型,其中有一個生產(chǎn)者(在Main方法中模擬)和多個消費者(在這個例子中是三個線程,每個線程代表一個消費者)。這個模型通過共享的任務隊列來同步生產(chǎn)者和消費者之間的工作。
代碼功能概述:
任務隊列:使用
Queue<Action>來存儲待執(zhí)行的任務(在這里,任務被定義為無參數(shù)的Action委托)。生產(chǎn)者:在
Main方法中,通過無限循環(huán)不斷生成新的任務(打印一個隨機數(shù)到控制臺),并將這些任務添加到任務隊列中。生產(chǎn)者每生成一個任務后,會隨機等待一段時間(模擬工作負載)。消費者:有三個消費者線程,每個線程都執(zhí)行相同的
DoWork方法,但傳入不同的控制臺顏色參數(shù)。消費者線程從任務隊列中取出任務并執(zhí)行(即調(diào)用任務委托),同時在執(zhí)行任務前將控制臺顏色設(shè)置為自己的顏色。如果任務隊列為空,消費者線程將等待(通過newTaskAvailable.WaitOne()),直到生產(chǎn)者通知它們有新的任務可用。同步機制:
- 使用
queueLock對象來同步對任務隊列的訪問,確保在添加或移除任務時不會發(fā)生數(shù)據(jù)競爭。 - 使用
newTaskAvailable(AutoResetEvent)來通知消費者有新的任務可用。當生產(chǎn)者將任務放入隊列后,它會設(shè)置這個事件,從而喚醒一個等待的消費者線程。 - 使用
consoleLock對象來同步對控制臺顏色的設(shè)置,防止多個消費者線程同時更改控制臺顏色。
- 使用
代碼執(zhí)行流程:
初始化:在
Main方法中,創(chuàng)建并啟動三個消費者線程,每個線程都執(zhí)行DoWork方法,但傳入不同的控制臺顏色參數(shù)。生產(chǎn)者循環(huán):
Main方法進入一個無限循環(huán),不斷生成新的任務(打印隨機數(shù)到控制臺),并將這些任務添加到任務隊列中。每生成一個任務后,生產(chǎn)者會隨機等待一段時間。消費者工作:每個消費者線程都進入一個無限循環(huán),嘗試從任務隊列中取出任務。如果隊列不為空,它們將設(shè)置控制臺顏色為自己的顏色,執(zhí)行任務(打印隨機數(shù)),然后再次嘗試從隊列中取出任務。如果隊列為空,它們將等待
newTaskAvailable事件被設(shè)置,這通常發(fā)生在生產(chǎn)者將新任務添加到隊列后。持續(xù)運行:這個過程將持續(xù)進行,因為生產(chǎn)者和消費者都運行在無限循環(huán)中。在實際應用中,您可能需要添加某種退出機制來優(yōu)雅地停止程序。
ManualResetEvent
class MainClass
{
// the array of consumer threads
private static List<Thread> consumers = new List<Thread> ();
// the task queue
private static Queue<Action> tasks = new Queue<Action>();
// the synchronisation object for locking the task queue
private static readonly object queueLock = new object();
// this wait handle notifies consumers of a new task
private static EventWaitHandle newTaskAvailable = new AutoResetEvent (false);
// this wait handle pauses consumers
private static EventWaitHandle pauseConsumers = new ManualResetEvent (true);
// the synchronisation object for locking the console color
private static readonly object consoleLock = new object();
// enqueue a new task
private static void EnqueueTask (Action task)
{
lock (queueLock)
{
tasks.Enqueue (task);
}
newTaskAvailable.Set();
}
// thread work method for consumers
private static void DoWork(ConsoleColor color)
{
while (true)
{
// check if producer asked us to pause
pauseConsumers.WaitOne ();
// get a new task
Action task = null;
lock (queueLock) {
if (tasks.Count > 0)
{
task = tasks.Dequeue ();
}
}
if (task != null)
{
// set console to this task's color
lock (consoleLock)
{
Console.ForegroundColor = color;
}
// execute task
task ();
}
else
// queue is empty - wait for a new task
newTaskAvailable.WaitOne();
}
}
public static void Main (string[] args)
{
// set up 3 consumers
consumers.Add(new Thread ( () => { DoWork(ConsoleColor.Red); }));
consumers.Add(new Thread ( () => { DoWork(ConsoleColor.Green); }));
consumers.Add(new Thread ( () => { DoWork(ConsoleColor.Blue); }));
// start all consumers
consumers.ForEach ( (t) => { t.Start (); });
bool consumersPaused = false;
while (true)
{
// add a new task
Random r = new Random();
EnqueueTask ( () => {
// the task is to write a random number to the console
int number = r.Next (10);
Console.Write (number);
});
// random sleep to simulate workload
Thread.Sleep (r.Next (1000));
// pressing any key pauses/unpauses the consumers
if (Console.KeyAvailable)
{
Console.Read ();
if (consumersPaused)
{
pauseConsumers.Set ();
Console.WriteLine ("Consumers resumed");
}
else
{
pauseConsumers.Reset ();
Console.WriteLine ("Consumers paused");
}
consumersPaused = !consumersPaused;
}
}
}
}
這段代碼實現(xiàn)了一個帶有暫停/恢復功能的生產(chǎn)者-消費者模型,其中包含一個生產(chǎn)者(在Main方法中模擬)和三個消費者線程。這個模型通過共享的任務隊列來同步生產(chǎn)者和消費者之間的工作,并且允許用戶通過按鍵操作來暫?;蚧謴拖M者的執(zhí)行。
代碼功能概述:
任務隊列:使用
Queue<Action>來存儲待執(zhí)行的任務。同步機制:
queueLock:用于同步對任務隊列的訪問,確保在添加或移除任務時不會發(fā)生數(shù)據(jù)競爭。newTaskAvailable(AutoResetEvent):當生產(chǎn)者將新任務添加到隊列時,設(shè)置此事件以通知一個等待的消費者線程。pauseConsumers(ManualResetEvent):允許生產(chǎn)者(或用戶)暫停和恢復消費者的執(zhí)行。consoleLock:用于同步對控制臺顏色的設(shè)置,防止多個消費者線程同時更改控制臺顏色。
消費者線程:三個消費者線程分別執(zhí)行
DoWork方法,但傳入不同的控制臺顏色參數(shù)。消費者線程無限循環(huán)地等待新任務,如果任務隊列中有任務,則取出任務并執(zhí)行,同時設(shè)置控制臺顏色為自己的顏色。如果任務隊列為空,則等待newTaskAvailable事件被設(shè)置。此外,消費者還會檢查pauseConsumers事件是否被設(shè)置,如果被設(shè)置,則暫停執(zhí)行直到被重置。生產(chǎn)者:在
Main方法中模擬,不斷生成新的任務(打印一個隨機數(shù)到控制臺)并添加到任務隊列中。每生成一個任務后,生產(chǎn)者會隨機等待一段時間(模擬工作負載)。此外,生產(chǎn)者還檢查控制臺是否有按鍵輸入,如果有,則切換消費者的暫停/恢復狀態(tài)。
代碼執(zhí)行流程:
初始化:創(chuàng)建并啟動三個消費者線程,每個線程都執(zhí)行
DoWork方法,但傳入不同的控制臺顏色參數(shù)。生產(chǎn)者循環(huán):
Main方法進入一個無限循環(huán),不斷生成新的任務(打印隨機數(shù)到控制臺),并將這些任務添加到任務隊列中。每生成一個任務后,生產(chǎn)者會隨機等待一段時間。同時,生產(chǎn)者檢查控制臺是否有按鍵輸入,并根據(jù)按鍵切換消費者的暫停/恢復狀態(tài)。消費者工作:每個消費者線程都進入一個無限循環(huán),首先檢查
pauseConsumers事件是否被設(shè)置,如果被設(shè)置則等待。然后嘗試從任務隊列中取出任務,如果隊列不為空,則設(shè)置控制臺顏色并執(zhí)行任務;如果隊列為空,則等待newTaskAvailable事件被設(shè)置。暫停/恢復:用戶可以通過按任意鍵來切換消費者的暫停/恢復狀態(tài)。如果消費者當前是暫停狀態(tài),則按鍵將喚醒它們并繼續(xù)執(zhí)行;如果消費者當前是運行狀態(tài),則按鍵將使它們暫停執(zhí)行。
CountdownEvent
class MainClass
{
// the array of consumer threads
private static List<Thread> consumers = new List<Thread> ();
// the task queue
private static Queue<Action> tasks = new Queue<Action>();
// the synchronisation object for locking the task queue
private static readonly object queueLock = new object();
// this wait handle notifies consumers of a new task
private static EventWaitHandle newTaskAvailable = new AutoResetEvent (false);
// the wait handle to quit consumers
private static CountdownEvent quitConsumers = new CountdownEvent (3);
// the flag to request that consumers quit
private static bool quitRequested = false;
// the synchronisation object for quitting consumers
private static readonly object quitLock = new object ();
// the synchronisation object for locking the console color
private static readonly object consoleLock = new object();
// enqueue a new task
private static void EnqueueTask (Action task)
{
lock (queueLock)
{
tasks.Enqueue (task);
}
newTaskAvailable.Set();
}
// thread work method for consumers
private static void DoWork(ConsoleColor color)
{
while (true)
{
// check if someone asked us to quit
lock (quitLock)
{
if (quitRequested)
{
Console.WriteLine ("Consumer {0} is quitting", color);
quitConsumers.Signal ();
break;
}
}
// get a new task
Action task = null;
lock (queueLock) {
if (tasks.Count > 0)
{
task = tasks.Dequeue ();
}
}
if (task != null)
{
// set console to this task's color
lock (consoleLock)
{
Console.ForegroundColor = color;
}
// execute task
task ();
}
else
// queue is empty - wait for a new task
newTaskAvailable.WaitOne(1000);
}
}
public static void Main (string[] args)
{
// set up 3 consumers
consumers.Add(new Thread ( () => { DoWork(ConsoleColor.Red); }));
consumers.Add(new Thread ( () => { DoWork(ConsoleColor.Green); }));
consumers.Add(new Thread ( () => { DoWork(ConsoleColor.Blue); }));
// start all consumers
consumers.ForEach ( (t) => { t.Start (); });
int iterations = 0;
while (true)
{
// add a new task
Random r = new Random();
EnqueueTask ( () => {
// the task is to write a random number to the console
int number = r.Next (10);
Console.Write (number);
});
// random sleep to simulate workload
Thread.Sleep (r.Next (1000));
// quit after 10 iterations
if (iterations++ >= 10)
{
// request consumer quit
lock (quitLock)
{
quitRequested = true;
}
// wait until all consumers have signalled
quitConsumers.Wait ();
Console.WriteLine ("All consumers have quit");
break;
}
}
}
}
這個代碼實現(xiàn)了一個帶有優(yōu)雅退出機制的生產(chǎn)者-消費者模型。它創(chuàng)建了三個消費者線程,這些線程從共享的任務隊列中取出并執(zhí)行任務。與之前的代碼相比,這個代碼添加了以下主要功能和改進:
優(yōu)雅退出機制:
- 引入了
quitRequested標志和quitLock同步對象,用于控制消費者線程的退出請求。 - 使用
CountdownEvent(quitConsumers)來等待所有消費者線程都完成退出前的清理工作并發(fā)出信號。 - 當生產(chǎn)者決定退出時,它會將
quitRequested標志設(shè)置為true,并通過quitConsumers.Wait()等待所有消費者線程都調(diào)用quitConsumers.Signal()來確認它們已經(jīng)準備好退出。
- 引入了
消費者線程的退出邏輯:
- 每個消費者線程在循環(huán)開始時都會檢查
quitRequested標志。如果設(shè)置為true,則消費者將打印一條退出消息,調(diào)用quitConsumers.Signal()來通知生產(chǎn)者它已準備好退出,并退出循環(huán)。 - 注意,消費者在退出前會釋放控制臺顏色的鎖(
consoleLock),但在這個特定的例子中,由于退出是在沒有任務可執(zhí)行的空閑時間發(fā)生的,所以這一步實際上可能是多余的,因為退出時不會再次訪問控制臺顏色。
- 每個消費者線程在循環(huán)開始時都會檢查
任務隊列的輪詢:
- 消費者在任務隊列為空時會調(diào)用
newTaskAvailable.WaitOne(1000),這是一個帶超時的等待調(diào)用。這意味著如果1000毫秒內(nèi)沒有新任務到來,消費者將停止等待并再次檢查退出條件。這有助于防止消費者線程在隊列為空時永久掛起。
- 消費者在任務隊列為空時會調(diào)用
生產(chǎn)者的迭代次數(shù)限制:
- 生產(chǎn)者在一個循環(huán)中運行,但只執(zhí)行有限次數(shù)的迭代(在這個例子中是10次)。每次迭代都會向任務隊列中添加一個新任務,并在迭代之間隨機等待一段時間以模擬工作負載。
- 當達到迭代次數(shù)限制時,生產(chǎn)者會請求消費者退出,并等待所有消費者都準備好退出。
其他同步機制:
- 代碼仍然使用
queueLock來同步對任務隊列的訪問,以防止數(shù)據(jù)競爭。 consoleLock用于同步對控制臺顏色的訪問,以確保當多個消費者線程嘗試同時更改控制臺顏色時不會發(fā)生沖突。
- 代碼仍然使用
代碼執(zhí)行流程:
- 程序初始化三個消費者線程,并將它們啟動。
- 生產(chǎn)者在一個循環(huán)中運行,每次迭代都向任務隊列中添加一個新任務,并隨機等待一段時間。
- 當達到迭代次數(shù)限制時,生產(chǎn)者請求消費者退出,并等待它們確認。
- 一旦所有消費者都確認退出,生產(chǎn)者將打印一條消息,并退出程序。
Barrier
class MainClass
{
// wait handles to rendezvous threads
public static Barrier barrier = new Barrier(3, b => Console.WriteLine("All threads have reached the barrier."));
// thread work method
public static void DoWork()
{
for (int i = 0; i < 5; i++)
{
Console.Write(Thread.CurrentThread.ManagedThreadId + ": " + i + " ");
// rendezvous with other threads
barrier.SignalAndWait();
}
}
public static void Main(string[] args)
{
// start three threads
new Thread(DoWork).Start();
new Thread(DoWork).Start();
new Thread(DoWork).Start();
// Keep the main thread alive to prevent the program from exiting before the threads finish
Console.WriteLine("Press Enter to exit...");
Console.ReadLine();
}
}
代碼功能概述:
Barrier 初始化:
- 在
MainClass中,創(chuàng)建了一個Barrier實例barrier,其構(gòu)造函數(shù)接受兩個參數(shù):參與同步的線程數(shù)(這里是 3)和一個在每次所有線程都到達屏障時調(diào)用的委托(打印一條消息)。
- 在
線程工作方法:
DoWork方法是三個線程將執(zhí)行的方法。每個線程都會進入一個循環(huán),循環(huán) 5 次。- 在每次循環(huán)迭代中,線程都會打印其當前迭代次數(shù)和線程 ID,然后調(diào)用
barrier.SignalAndWait()。 SignalAndWait方法導致當前線程在屏障處等待,直到所有其他參與線程也都調(diào)用了SignalAndWait。一旦所有線程都到達屏障,它們會同時繼續(xù)執(zhí)行,并且如果提供了,會執(zhí)行構(gòu)造函數(shù)中指定的委托(打印一條消息)。
主線程:
- 主線程啟動了三個
DoWork線程,并等待用戶按下 Enter 鍵以繼續(xù)執(zhí)行。這是為了防止主線程在后臺線程完成之前退出程序。
- 主線程啟動了三個
運行結(jié)果
- 當所有三個線程都到達
barrier.SignalAndWait()時,它們會同時停止執(zhí)行,并等待彼此。 - 一旦所有線程都到達屏障,它們會同時繼續(xù)執(zhí)行,并且控制臺會打印出 “All threads have reached the barrier.”。
- 這個過程會在每次循環(huán)迭代時重復,直到每個線程都完成了 5 次迭代。
- 最后,用戶按下 Enter 鍵后,程序退出。
到此這篇關(guān)于C#多線程同步不同實現(xiàn)方式小結(jié)的文章就介紹到這了,更多相關(guān)C# 多線程同步內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

