詳解C# 線程的掛起與喚醒
如果說(shuō)C#和C++有什么不同,博主不得不說(shuō),對(duì)于異步的支持程度是C#的一一個(gè)偉大的進(jìn)步。
其實(shí)早期的C++都沒(méi)有異步,并發(fā)的概念。博主第一次使用C++創(chuàng)建異步程序的時(shí)候,是使用boost庫(kù)的內(nèi)容進(jìn)行實(shí)現(xiàn)的。相對(duì)而言,C#對(duì)于異步的支持可以說(shuō)是相當(dāng)?shù)暮?。相信很多名詞大家都很耳熟能詳,比如說(shuō)Thread,BeginInvoke,Delegate,backgroundworker等等。。。其實(shí)樓主在使用了這么多的異步操作過(guò)程中,還是覺(jué)得backgroudworker比較好用。
當(dāng)然,我們今天要說(shuō)的和上面的無(wú)關(guān)。講述的是如何在線程中進(jìn)行掛起喚醒操作。
假設(shè),有一個(gè)Thread現(xiàn)在需要掛起,等到合適的時(shí)候再喚醒那么這個(gè)線程(消費(fèi)者模式)。如果大家需要用Suspend,Resume操作,我建議還是要思考再三。以下是msdn原話(https://msdn.microsoft.com/zh-cn/library/system.threading.thread.suspend(v=vs.110).aspx):
Do not use the Suspend and Resume methods to synchronize the activities of threads. You have no way of knowing what code a thread is executing when you suspend it. If you suspend a thread while it holds locks during a security permission evaluation, other threads in the AppDomain might be blocked. If you suspend a thread while it is executing a class constructor, other threads in the AppDomain that attempt to use that class are blocked. Deadlocks can occur very easily.
本篇文章要說(shuō)的線程掛起與繼續(xù)的方式其實(shí)是利用AutoResetEvent和ManualResetEvent的方法進(jìn)行堵塞和繼續(xù)的。
在介紹AutoResetEvent和ManualResetEvent之前,先介紹一個(gè)概念,就是線程中Set()和Reset()的區(qū)別。
- set:指的是將一個(gè)事件設(shè)置為有信號(hào),那么被這個(gè)事件堵塞的線程就會(huì)繼續(xù)下去。
- reset:指的是將一個(gè)事件設(shè)置為無(wú)信號(hào),那么嘗試?yán)^續(xù)的事件就會(huì)被堵塞。
一,AutoResetEvent類(lèi)
這個(gè)類(lèi)的字面意思就能夠解釋一切:自動(dòng)reset的事件,就是這個(gè)事件一旦set之后,如果某個(gè)線程堵塞被繼續(xù)了,那么就會(huì)自動(dòng)reset。下一次如果嘗試?yán)^續(xù),依然會(huì)被堵塞。
其中AutoResetEvent類(lèi)的構(gòu)造函數(shù)有一個(gè)參數(shù) 是bool型。
MSDN的解釋是:
Initializes a new instance of the AutoResetEvent class with a Boolean value indicating whether to set the initial state to signaled.
如果這個(gè)參數(shù)是true,那么第一次嘗試?yán)^續(xù)就不會(huì)被阻塞。如果這個(gè)參數(shù)是false,那么第一次嘗試?yán)^續(xù)就會(huì)被堵塞。
以下是測(cè)試代碼,取自MSDN:
using System; using System.Threading; // Visual Studio: Replace the default class in a Console project with // the following class. class Example { private static AutoResetEvent event_1 = new AutoResetEvent(true); private static AutoResetEvent event_2 = new AutoResetEvent(false); static void Main() { Console.WriteLine("Press Enter to create three threads and start them.\r\n" + "The threads wait on AutoResetEvent #1, which was created\r\n" + "in the signaled state, so the first thread is released.\r\n" + "This puts AutoResetEvent #1 into the unsignaled state."); Console.ReadLine(); for (int i = 1; i < 4; i++) { Thread t = new Thread(ThreadProc); t.Name = "Thread_" + i; t.Start(); } Thread.Sleep(250); for (int i = 0; i < 2; i++) { Console.WriteLine("Press Enter to release another thread."); Console.ReadLine(); event_1.Set(); Thread.Sleep(250); } Console.WriteLine("\r\nAll threads are now waiting on AutoResetEvent #2."); for (int i = 0; i < 3; i++) { Console.WriteLine("Press Enter to release a thread."); Console.ReadLine(); event_2.Set(); Thread.Sleep(250); } // Visual Studio: Uncomment the following line. //Console.Readline(); } static void ThreadProc() { string name = Thread.CurrentThread.Name; Console.WriteLine("{0} waits on AutoResetEvent #1.", name); event_1.WaitOne(); Console.WriteLine("{0} is released from AutoResetEvent #1.", name); Console.WriteLine("{0} waits on AutoResetEvent #2.", name); event_2.WaitOne(); Console.WriteLine("{0} is released from AutoResetEvent #2.", name); Console.WriteLine("{0} ends.", name); } }
其中,AutoResetEvent.WaitOne()這個(gè)方法就是線程中嘗試?yán)^續(xù)。如果沒(méi)有SET信號(hào),那么就會(huì)一直阻塞,如果收到Set信號(hào)該線程就會(huì)繼續(xù)。但是因?yàn)槭茿utoResetEvent,所以下一次waitOne依然會(huì)被阻塞。
上面代碼的輸出結(jié)果是:
Press Enter to create three threads and start them. The threads wait on AutoResetEvent #1, which was created in the signaled state, so the first thread is released. This puts AutoResetEvent #1 into the unsignaled state. Thread_1 waits on AutoResetEvent #1. Thread_1 is released from AutoResetEvent #1. Thread_1 waits on AutoResetEvent #2. Thread_3 waits on AutoResetEvent #1. Thread_2 waits on AutoResetEvent #1. Press Enter to release another thread. Thread_3 is released from AutoResetEvent #1. Thread_3 waits on AutoResetEvent #2. Press Enter to release another thread. Thread_2 is released from AutoResetEvent #1. Thread_2 waits on AutoResetEvent #2. All threads are now waiting on AutoResetEvent #2. Press Enter to release a thread. Thread_2 is released from AutoResetEvent #2. Thread_2 ends. Press Enter to release a thread. Thread_1 is released from AutoResetEvent #2. Thread_1 ends. Press Enter to release a thread. Thread_3 is released from AutoResetEvent #2. Thread_3 ends.
二,ManualResetEvent
ManualResetEvent和AutoResetEvent大部分概念都是相同的,最大的不同就是一個(gè)是自動(dòng)reset一個(gè)是手動(dòng)reset。也就是說(shuō),如果使用ManualResetEvent類(lèi),一旦Set之后,所有已經(jīng)阻塞的線程(waitone())都會(huì)繼續(xù)。而且之后調(diào)用waitone的線程也不會(huì)被堵塞,除非手動(dòng)再次Reset。也就是說(shuō),這個(gè)類(lèi)是手動(dòng)開(kāi)啟關(guān)閉信號(hào)的事件。
以下是測(cè)試代碼,取自MSDN:
using System; using System.Threading; public class Example { // mre is used to block and release threads manually. It is // created in the unsignaled state. private static ManualResetEvent mre = new ManualResetEvent(false); static void Main() { Console.WriteLine("\nStart 3 named threads that block on a ManualResetEvent:\n"); for(int i = 0; i <= 2; i++) { Thread t = new Thread(ThreadProc); t.Name = "Thread_" + i; t.Start(); } Thread.Sleep(500); Console.WriteLine("\nWhen all three threads have started, press Enter to call Set()" + "\nto release all the threads.\n"); Console.ReadLine(); mre.Set(); Thread.Sleep(500); Console.WriteLine("\nWhen a ManualResetEvent is signaled, threads that call WaitOne()" + "\ndo not block. Press Enter to show this.\n"); Console.ReadLine(); for(int i = 3; i <= 4; i++) { Thread t = new Thread(ThreadProc); t.Name = "Thread_" + i; t.Start(); } Thread.Sleep(500); Console.WriteLine("\nPress Enter to call Reset(), so that threads once again block" + "\nwhen they call WaitOne().\n"); Console.ReadLine(); mre.Reset(); // Start a thread that waits on the ManualResetEvent. Thread t5 = new Thread(ThreadProc); t5.Name = "Thread_5"; t5.Start(); Thread.Sleep(500); Console.WriteLine("\nPress Enter to call Set() and conclude the demo."); Console.ReadLine(); mre.Set(); // If you run this example in Visual Studio, uncomment the following line: //Console.ReadLine(); } private static void ThreadProc() { string name = Thread.CurrentThread.Name; Console.WriteLine(name + " starts and calls mre.WaitOne()"); mre.WaitOne(); Console.WriteLine(name + " ends."); } }
輸出結(jié)果是:
Start 3 named threads that block on a ManualResetEvent: Thread_0 starts and calls mre.WaitOne() Thread_1 starts and calls mre.WaitOne() Thread_2 starts and calls mre.WaitOne() When all three threads have started, press Enter to call Set() to release all the threads. Thread_2 ends. Thread_0 ends. Thread_1 ends. When a ManualResetEvent is signaled, threads that call WaitOne() do not block. Press Enter to show this. Thread_3 starts and calls mre.WaitOne() Thread_3 ends. Thread_4 starts and calls mre.WaitOne() Thread_4 ends. Press Enter to call Reset(), so that threads once again block when they call WaitOne(). Thread_5 starts and calls mre.WaitOne() Press Enter to call Set() and conclude the demo. Thread_5 ends.
ManualResetEvent類(lèi)的輸出結(jié)果與AutoResetEvent輸出結(jié)果最大的不同是在于:
如果不手動(dòng)Reset,一旦調(diào)用Set方法,那么ManualResetEvent.WaitOne()就不會(huì)堵塞。
但是,AutoResetEvent會(huì)自動(dòng)Reset,所以哪怕不手動(dòng)Reset,每一次AutoResetEvent.WaitOne()都需要Set方法進(jìn)行觸發(fā)以繼續(xù)線程。
以上就是詳解C# 線程的掛起與喚醒的詳細(xì)內(nèi)容,更多關(guān)于C# 線程的掛起與喚醒的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
使用C#來(lái)編寫(xiě)一個(gè)異步的Socket服務(wù)器
這篇文章主要介紹了使用C#來(lái)編寫(xiě)一個(gè)異步的Socket服務(wù)器,通過(guò)無(wú)阻塞機(jī)制來(lái)獲取更高的處理效率,需要的朋友可以參考下2015-07-07Jquery+Ajax+Json+存儲(chǔ)過(guò)程實(shí)現(xiàn)高效分頁(yè)
這篇文章主要介紹Jquery+Ajax+Json+存儲(chǔ)過(guò)程實(shí)現(xiàn)分頁(yè),需要的朋友可以參考下2015-08-08.NET/C# 使用Stopwatch測(cè)量運(yùn)行時(shí)間
這篇文章主要介紹了.NET/C# 使用Stopwatch測(cè)量運(yùn)行時(shí)間,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-01-01C#驗(yàn)證控件validator的簡(jiǎn)單使用
這篇文章主要介紹了C#驗(yàn)證控件validator的簡(jiǎn)單使用方法和示例,十分的簡(jiǎn)單實(shí)用,有需要的小伙伴可以參考下。2015-06-06C# Winform調(diào)用百度接口實(shí)現(xiàn)人臉識(shí)別教程(附源碼)
這篇文章主要介紹了C# Winform調(diào)用百度接口實(shí)現(xiàn)人臉識(shí)別教程,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-05-05C# CAD SelectionFilter下TypedValue數(shù)組使用方式
這篇文章主要介紹了C# CAD SelectionFilter下TypedValue數(shù)組使用方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-02-02C#?winform?窗體控件跨線程訪問(wèn)的實(shí)現(xiàn)
在做winform開(kāi)發(fā)時(shí),如果在子線程中去設(shè)置主線程中UI控件的屬性,會(huì)出現(xiàn)“跨線程調(diào)用異?!?本文就來(lái)介紹一下C#?winform?窗體控件跨線程訪問(wèn)的實(shí)現(xiàn),感興趣的可以了解一下2023-12-12