前言
众所周知,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()