• C# 自定义Thread挂起线程和恢复线程


    前言

    众所周知,在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 的介绍

     名称说明
    公共方法由 XNA Framework 提供支持1. Close 在派生类中被重写时,释放由当前 WaitHandle 持有的所有资源。 (继承自 WaitHandle。)
    在XNA Framework中,此成员由 Close() 重写。
    公共方法2. CreateObjRef 创建一个对象,该对象包含生成用于与远程对象进行通信的代理所需的全部相关信息。 (继承自 MarshalByRefObject。)
    公共方法3. Dispose() 释放由 WaitHandle 类的当前实例使用的所有资源。 (继承自 WaitHandle。)
    公共方法由 XNA Framework 提供支持4. Equals(Object) 确定指定的对象是否等于当前对象。 (继承自 Object。)
    公共方法5. GetAccessControl 获取 EventWaitHandleSecurity 对象,该对象表示由当前 EventWaitHandle 对象表示的已命名系统事件的访问控制安全性。 (继承自 EventWaitHandle。)
    公共方法由 XNA Framework 提供支持6. GetHashCode 作为默认哈希函数。 (继承自 Object。)
    公共方法8. GetLifetimeService 检索控制此实例的生存期策略的当前生存期服务对象。 (继承自 MarshalByRefObject。)
    公共方法由 XNA Framework 提供支持9. GetType 获取当前实例的 Type (继承自 Object。)
    公共方法10. InitializeLifetimeService 获取控制此实例的生存期策略的生存期服务对象。 (继承自 MarshalByRefObject。)
    公共方法11. Reset 将事件状态设置为非终止状态,导致线程阻止。 (继承自 EventWaitHandle。)
    公共方法由 XNA Framework 提供支持12. Set 将事件状态设置为终止状态,允许一个或多个等待线程继续。 (继承自 EventWaitHandle。)
    公共方法13. SetAccessControl 设置已命名的系统事件的访问控制安全性。 (继承自 EventWaitHandle。)
    公共方法由 XNA Framework 提供支持14. ToString 返回表示当前对象的字符串。 (继承自 Object。)
    公共方法由 XNA Framework 提供支持15. WaitOne() 阻止当前线程,直到当前 WaitHandle 收到信号。 (继承自 WaitHandle。)
    在XNA Framework中,此成员由 WaitOne() 重写。
    公共方法16. WaitOne(Int32) 阻止当前线程,直到当前 WaitHandle 收到信号,同时使用 32 位带符号整数指定时间间隔。 (继承自 WaitHandle。)
    公共方法17. WaitOne(TimeSpan) 阻止当前线程,直到当前实例收到信号,同时使用 TimeSpan 指定时间间隔。 (继承自 WaitHandle。)
    公共方法由 XNA Framework 提供支持18. WaitOne(Int32, Boolean) 阻止当前线程,直到当前的 WaitHandle 收到信号为止,同时使用 32 位带符号整数指定时间间隔,并指定是否在等待之前退出同步域。 (继承自 WaitHandle。)
    在XNA Framework中,此成员由 WaitOne(Int32, Boolean) 重写。
    公共方法19. WaitOne(TimeSpan, Boolean) 使用 TimeSpan 指定时间间隔并指定是否在等待之前退出同步域,以此阻止当前线程,直到当前的实例收到信号。 (继承自 WaitHandle。)

    出处:https://docs.microsoft.com/zh-cn/dotnet/api/system.threading.manualresetevent?view=net-5.0#methods

  • 相关阅读:
    JavaScript cookie详解
    Javascript数组的排序:sort()方法和reverse()方法
    javascript中write( ) 和 writeln( )的区别
    div做表格
    JS 盒模型 scrollLeft, scrollWidth, clientWidth, offsetWidth 详解
    Job for phpfpm.service failed because the control process exited with error code. See "systemctl status phpfpm.service" and "journalctl xe" for details.
    orm查询存在价格为空问题
    利用救援模式破解系统密码
    SSH服务拒绝了密码
    C# 调用 C++ DLL 中的委托,引发“对XXX::Invoke类型的已垃圾回收委托进行了回调”错误的解决办法
  • 原文地址:https://www.cnblogs.com/mq0036/p/14205316.html
Copyright © 2020-2023  润新知