前言
眾所周知,Thread類中的掛起線程和恢復線程微軟已標記過時,因為可能會造成問題
Resume() 恢復當前線程 |
已過時。 Resumes a thread that has been suspended. |
Suspend() 掛起當前線程 |
已過時。 掛起線程,或者如果線程已掛起,則不起作用。 |
其他方式實現
一、ThreadWorkItem
class ThreadWorkItem { public int ThreadManagerId { get; set; } public Thread Thread { get; set; } public string ThreadName { get; set; } public bool Flag { 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.Flag) { 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 { Flag = false, Thread = t, ThreadManagerId = t.ManagedThreadId, ThreadName = t.Name }; Works.Add(wItem); t.Start(Works[i]); } //5秒后允許一個等待的線程繼續。當前允許的是線程1 Thread.Sleep(5000); Works[0].Flag = true; Console.WriteLine($"thread-{Works[0].ThreadName} is 暫停"); //5秒后允許一個等待的線程繼續。當前允許的是線程0,1 Thread.Sleep(5000); Works[0].Flag = 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.實例化信號量的構造參數是什么意思?
答:信號量非終止狀態,如果值為false則表明終止狀態,調用WaitOne()方法的時候立即阻塞。設置true可以理解為隱式調用了Set()方法放行一次。
4. ManualResetEvent和AutoResetEvent的區別
答:ManualResetEvent調用Set()允許一個或多個被同一個ManualResetEvent實例WaitOne()的線程放行。
AutoResetEvent調用Set() 只能允許一個線程放行。如果多處使用同一個實例,則需要手動調用多次Set()