前言
眾所周知,在Thread類中的掛起線程和恢復線程微軟已標記過時的,因為可能會造成問題,具體的可以自己去看官方介紹,或查看我的前面幾篇博客:
Thread | Resume() 恢復當前線程 |
已過時。 Resumes a thread that has been suspended. |
Thread | Suspend() 掛起當前線程 |
已過時。 掛起線程,或者如果線程已掛起,則不起作用。 |
其他方式實現
一、自定義ThreadWorkItem類
class ThreadWorkItem { public int ThreadManagerId { get; set; } public Thread Thread { get; set; } public string ThreadName { get; set; } public bool StopFlag { get; set; } public ManualResetEvent ManualResetEvent { get; set; } }
二、C# Thread掛起線程和恢復線程的實現的兩種方式
方式1:使用變量開關控制掛起線程和恢復線程,具體代碼如下

public class Program { //線程工作集合 private static List<ThreadWorkItem> Works = new List<ThreadWorkItem>(); //方式1:使用變量開關控制掛起線程和恢復線程 private static void Main(string[] args) { ThreadWorkItem wItem = null; Thread t = null; var threadNum = 2; for (int i = 0; i < threadNum; i++) { t = new Thread(o=> { var w = o as ThreadWorkItem; if (w == null) return; while (true) { if (!w.StopFlag) { Console.WriteLine("我是線程:" + Thread.CurrentThread.Name); Thread.Sleep(1000); continue; } //避免CPU空轉 Thread.Sleep(5000); } }); //$ C#6.0語法糖 t.Name = $"Hello I'am 線程:{i}-{t.ManagedThreadId}"; wItem = new ThreadWorkItem { StopFlag = false, Thread = t, ThreadManagerId = t.ManagedThreadId, ThreadName = t.Name }; Works.Add(wItem); t.Start(Works[i]); } //5秒后允許一個等待的線程繼續。當前允許的是線程1 Thread.Sleep(5000); Works[0].StopFlag = true; Console.WriteLine($"thread-{Works[0].ThreadName} is 暫停"); //5秒后允許一個等待的線程繼續。當前允許的是線程0,1 Thread.Sleep(5000); Works[0].StopFlag = false; Console.WriteLine($"thread-{Works[0].ThreadName} is 恢復"); } }
方式2:使用ManualResetEvent控制掛起線程和恢復線程(推薦);替代Thread類中被微軟標記過時的函數(內核模式非混合模式)

public class Program { //線程工作集合 static List<ThreadWorkItem> Works = new List<ThreadWorkItem>(); //方式2:使用ManualResetEvent控制掛起線程和恢復線程(推薦);替代Thread類中被微軟標記過時的函數 static void Main(string[] args) { Task.Factory.StartNew(() => { Thread t = null; ThreadWorkItem item = null; for (int i = 0; i < 2; i++) { t = new Thread((o) => { var w = o as ThreadWorkItem; if (w == null) return; while (true) { //阻塞當前線程 w.ManualResetEvent.WaitOne(); Console.WriteLine("我是線程:" + Thread.CurrentThread.Name); Thread.Sleep(1000); } }); t.Name = "Hello,i 'am Thread: " + i; item = new ThreadWorkItem { //線程0,1持續運行,設置true后非阻塞,持續運行。需要手動觸發Reset()才會阻塞實例所在當前線程 ManualResetEvent = new ManualResetEvent(true), Thread = t, ThreadManagerId = t.ManagedThreadId, ThreadName = t.Name }; Works.Add(item); t.Start(item); } //5秒后准備暫停一個線程1。線程0持續運行 Thread.Sleep(5000); Console.WriteLine("close..."); Works[1].ManualResetEvent.Reset(); //5秒后恢復線程1;線程0,1持續運行 Thread.Sleep(5000); Console.WriteLine("open..."); Works[1].ManualResetEvent.Set(); //5秒后准備暫停一個線程0。線程1持續運行 Thread.Sleep(5000); Console.WriteLine("close0..."); Works[0].ManualResetEvent.Reset(); //5秒后恢復線程1;線程0,1持續運行 Thread.Sleep(5000); Console.WriteLine("open0..."); Works[0].ManualResetEvent.Set(); }); Console.ReadLine(); } }
三、總結
1.有時候會覺得必須由主線程創建ManualResetEvent實例才能起到作用,實際並不是這樣的,上述方式2則證明了這一點。
2.那么AutoResetEvent做不到同樣的效果嗎?
答:AutoResetEvent 顧名思義,自動Reset()表示重置信號量狀態,則當前線程中持有WaitOne()就又會被持續阻塞。而ManualResetEvent必須要手動調用Reset()才能重置信號量阻塞當前線程。
這里再解釋下Set(),它表明允許一個或多個被同一個ManualResetEvent實例WaitOne()的線程放行。
3.實例化信號量的構造參數是什么意思?如:new ManualResetEvent(true)
答:信號量的終止狀態,如果值為false則表明終止狀態,調用WaitOne()方法的時候立即阻塞。設置true可以理解為隱式調用了Set()方法放行一次。
4. ManualResetEvent和AutoResetEvent的區別
答:ManualResetEvent調用Set()允許一個或多個被同一個ManualResetEvent實例WaitOne()的線程放行。
AutoResetEvent調用Set() 只能允許一個線程放行。如果多處使用同一個實例,則需要手動調用多次Set(),並且會自動調用Reset方法
出處:https://www.cnblogs.com/gaobing/p/5124945.html
=======================================================================================
C# ManualResetEvent 的介紹
名稱 | 說明 | |
---|---|---|
![]() ![]() |
Close | 在派生類中被重寫時,釋放由當前 WaitHandle 持有的所有資源。 (繼承自 WaitHandle。) 在XNA Framework中,此成員由 Close() 重寫。 |
![]() |
CreateObjRef | 創建一個對象,該對象包含生成用於與遠程對象進行通信的代理所需的全部相關信息。 (繼承自 MarshalByRefObject。) |
![]() |
Dispose() | 釋放由 WaitHandle 類的當前實例使用的所有資源。 (繼承自 WaitHandle。) |
![]() ![]() |
Equals(Object) | 確定指定的對象是否等於當前對象。 (繼承自 Object。) |
![]() |
GetAccessControl | 獲取 EventWaitHandleSecurity 對象,該對象表示由當前 EventWaitHandle 對象表示的已命名系統事件的訪問控制安全性。 (繼承自 EventWaitHandle。) |
![]() ![]() |
GetHashCode | 作為默認哈希函數。 (繼承自 Object。) |
![]() |
GetLifetimeService | 檢索控制此實例的生存期策略的當前生存期服務對象。 (繼承自 MarshalByRefObject。) |
![]() ![]() |
GetType | 獲取當前實例的 Type。 (繼承自 Object。) |
![]() |
InitializeLifetimeService | 獲取控制此實例的生存期策略的生存期服務對象。 (繼承自 MarshalByRefObject。) |
![]() |
Reset | 將事件狀態設置為非終止狀態,導致線程阻止。 (繼承自 EventWaitHandle。) |
![]() ![]() |
Set | 將事件狀態設置為終止狀態,允許一個或多個等待線程繼續。 (繼承自 EventWaitHandle。) |
![]() |
SetAccessControl | 設置已命名的系統事件的訪問控制安全性。 (繼承自 EventWaitHandle。) |
![]() ![]() |
ToString | 返回表示當前對象的字符串。 (繼承自 Object。) |
![]() ![]() |
WaitOne() | 阻止當前線程,直到當前 WaitHandle 收到信號。 (繼承自 WaitHandle。) 在XNA Framework中,此成員由 WaitOne() 重寫。 |
![]() |
WaitOne(Int32) | 阻止當前線程,直到當前 WaitHandle 收到信號,同時使用 32 位帶符號整數指定時間間隔。 (繼承自 WaitHandle。) |
![]() |
WaitOne(TimeSpan) | 阻止當前線程,直到當前實例收到信號,同時使用 TimeSpan 指定時間間隔。 (繼承自 WaitHandle。) |
![]() ![]() |
WaitOne(Int32, Boolean) | 阻止當前線程,直到當前的 WaitHandle 收到信號為止,同時使用 32 位帶符號整數指定時間間隔,並指定是否在等待之前退出同步域。 (繼承自 WaitHandle。) 在XNA Framework中,此成員由 WaitOne(Int32, Boolean) 重寫。 |
![]() |
WaitOne(TimeSpan, Boolean) | 使用 TimeSpan 指定時間間隔並指定是否在等待之前退出同步域,以此阻止當前線程,直到當前的實例收到信號。 (繼承自 WaitHandle。) |
出處:https://docs.microsoft.com/zh-cn/dotnet/api/system.threading.manualresetevent?view=net-5.0#methods