C# 多线程演变历程,Thread和ThreadPool实现类似Task.Wait()效果的回调,线程顺序控制

重点是总结一些常用用法含义,个人体会他和委托密切相关,尤其是开启线程,初始化,
第一个知识点:线程使用

用法:new Thread(委托);

Thread线程开启,自定义方式解决线程执行完毕回调问题,达成Task.wait()的效果

Join()
官方有一句话:此方法更改调用线程的状态以包括 ThreadState.WaitSleepJoin。 不能对处于 ThreadState.Unstarted 状态的线程调用 Join。
不仅仅是只有这个方法,还有很多
thread1.Resume();//继续挂起的线程
thread1.Suspend();//挂起线程,暂停的意思
thread1.Abort();//终止线程
方法很多,但是实际上中看不中用,主要是线程本身是计算机的资源,不是程序凭空创造出来的数据对象,
造成很多的响应,不灵敏,可能申请停止,计算机操作系统并未及时停止,它要忙完自己的事情
这是.net frmwork 1.0提供的,存在这些问题

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace ThreadDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            Func<int> func1 = () =>
            {
                return DateTime.Now.Year;
            };

            //Thread开启线程,但并没有提供控制线程执行顺序的Api,如何做呢?
            //如果让某个线程
            Thread thread1 = new Thread(() => {
                Thread.Sleep(2000);
                Console.WriteLine("我是线程1");
            });
            //thread1.Start();
            //如何让线程1结束之后,才执行线程2
            //**********第一个方式:while()
            //while (thread1.ThreadState != ThreadState.Stopped)
            //{
            //    Thread.Sleep(100);
            //}
            //如果开启线程没有停止,阻塞当前主线程,即不执行线程2
            //这样带来一个问题,当前程序显示无响应状态,即卡顿状态
            Thread thread2 = new Thread(() =>
            {
             //***********第二个方式:Join()
                //阻塞线程1,查看官方文档即可知道它的作用
                //直到它执行完成,如果没有执行完,将一直阻塞,里面可以传入一个时间参数
                thread1.Join();


                Console.WriteLine("我是线程2");
            });
            //thread2.Start();
            //明显看出,即使线程1先开启,但是执行时间长,仍然是后执行输出,
            //仅仅是通过代码先后顺序,无法真正执行控制顺序

            //********第三种方式,两个委托,放在一个前台线程中执行
            //如下需要两个委托,顺序执行,并且依赖第一个委托的结果,如下获取年份,
            //调用方法,获取结果应该还是0,为啥,因为线程开启太慢,
            //方法中线程还没来得及开启
            //int res = CallReturn<int>(func1);
            Func<int> funres = CallReturnFunc<int>(func1);
            //这里实际上是让委托在子线程里执行,

            int res = funres.Invoke();
            //这里实际确保让子线程内容执行完成
        }

       
        //进一步如果要调用是带参数的返回呢?
        //定义一个方法,基于传递一个委托参数,方法体里面将这个委托执行的值,作为参数,传递给开启线程执行另外一个委托,
        //紧接着返回这个
        private static T CallReturn<T>(Func<T> func)
        {
            T t = default(T);
            //根据参数传进来的委托,执行完返回的值,进行接收
            //开启新线程,执行新委托
            //ThreadStart thread3 = new ThreadStart(()=>{
            //    t = func.Invoke();
            //});
            //Thread t3 = new Thread(thread3);
            //t3.Start(); 

            //一个多线程,像是异步的感觉
            Thread t3 = new Thread(() =>
            {
                t = func.Invoke();
            });
            t3.Start();
            return t;
        }

        //线程开启执行, 还没来得及执行完,主线程已经执行完毕,值未修改,优化如下:
        //返回一个委托,里面强制要求他执行完,借助join方法,返回这个委托后,
        //调用方法返回值委托的invoke方法即可

        //方法后面定义的T,后面才能使用
        private static Func<T> CallReturnFunc<T>(Func<T> func1) {
            T t=default(T);
            Thread thread = new Thread(() => {
                t = func1.Invoke();
            });
            thread.Start();
            //确保子线程执行完,返回一个委托,里面让join执行,
            //同时在外面方法调用返回委托的Invoke方法
            
            //return func1;
            return new Func<T>(() => {
                thread.Join();
                return t;
            });
        }

        //线程功能强大,但是使用起来并不友好,有点难使用,
        //问题的关键在于线程数量是没有控制的,在系统资源可用的情况下,可一直使用,
        //对于线程的开辟和销毁都是由程序员来控制,并且这个过程代价比较大
        //正是由于这一个特性,提供一个新的方法,即线程池方式,
        //线程池的思想,事先创建一部分的线程,放在线程中待用,
        //要用直接取来用,用完还回来即可
    }
}

这种原生的Thread方式,功能强大,不过掌握起来不太容易,很容易是得不偿失,原因是线程创建,关闭都是程序员操作,关键是平台没有办法限制,考虑线程创建销毁,极其占用资源,就像有一句说的,一个几岁小孩手持Ak步枪,使用不好,很有可能对自己造成伤害,另外一个思路应运而生,就是线程池,

简而言之是,不再是创建线程,而是从线程池中申请,取来用,用了还

第二个知识点:线程池,利用额外的ManualResetEvent事件,达成Task.wait()的效果

它实现顺序原理,基于flag标识,传进来一个标识对象MRE,一旦子线程里面完成,设置Set()一下
子线程方法里面,有一个参数,正好是MRE第二个参数,
外面通过MRE的一个方法WaitOne一直等待,等它执行完

使用语法很简单:.QueueUserWorkItem()里面接收一个委托,

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace ThreadPoolStart
{
    class Program
    {
//它实现顺序原理,基于flag标识,传进来一个标识对象MRE,一旦子线程里面完成,设置Set()一下
            //子线程方法里面,有一个参数,正好是MRE第二个参数,
            //外面通过MRE的一个方法WaitOne一直等待,等它执行完
        //https://docs.microsoft.com/zh-cn/dotnet/api/system.threading.waitcallback?view=netcore-3.1
        /*WaitCallback 表示要在 ThreadPool 线程上执行的回调方法。 通过向 WaitCallback 构造函数传递回调方法来创建委托。 你的方法必须具有此处显示的签名。
通过将 WaitCallback 委托传递给 ThreadPool.QueueUserWorkItem来将方法排队以便执行。 当线程池线程变得可用时,回调方法会执行。*/
        static void Main(string[] args)
        {
            //如果希望让线程池中线程等待,通过ManualResetEvent
            ManualResetEvent mre = new ManualResetEvent(false);
            //一直等待一个信号,有点监听的意思,等待谁呢?
            //等待mre的ret,这个信号执行完成


            //开启一个线程池,简单方法是直接传递一个方法
            //默认需要符合WaitCallBack委托的方法,实际上传值给这个委托类型,无参带返回值
            //第一种方式,直接一个带参无返回值的方法
            //这个方法,有点像是Invoke执行一个委托,
            //直接就是执行它
           bool b1= ThreadPool.QueueUserWorkItem(TestThreadPool,mre);
            //?????????传一个数据参数,反而不执行
           //Thread.Sleep(1);
            //new一个WaitCallBack委托
            WaitCallback callback = new WaitCallback((o) => {
                Console.WriteLine("我是委托实例中调用方法,传入数据:"+o.ToString());
                
            });
            //休眠1秒,仍然只有b2执行,因为b1还未执行完,随着主线程结束而结束
            bool b2 = ThreadPool.QueueUserWorkItem(callback,"2");
            //主线程休眠1ms,等待线程池中线程开始执行其中代码
            //由于线程中线程都是后台线程,主线程关闭相对应子线程都结束
            //Thread.Sleep(1000);

           /*收到信号后,ManualResetEvent 会一直发出信号,直到通过调用 Reset() 方法进行手动重置。 也就是说,对 WaitOne 的调用会立即返回。*/
            
            mre.WaitOne();
//这个方法同样是阻塞线程的

        }
        private static void TestThreadPool(object o){
            ManualResetEvent mre=(ManualResetEvent)o;
           
            Thread.Sleep(2000);
            Console.WriteLine("线程中线程执行回调方法!方法参数:"+o.ToString());
            mre.Set();
            //这种情况,该如何传递这个信号
        }
    }
}

线程池,这种池化的思想,就是做一个容器,提前先申请需要的5个线程,程序需要直接从里面取,用完放回容器,线程池负责维护控制状态,当然有需要他也会再创建,销毁需要的线程,简单说帮我们维护线程,克服线程使用不当引起巨大的破坏,比如海量申请,造成死机等等的影响,不过他的API很少,使用起来极其不方便,并且顺序控制体验仍然不好,waitone,仍然是卡顿界面,阻塞主线程的

当然实际多线程使用,使用更加广泛的是Task,下一篇再讲,它使用起来更加方便快捷,不过它的实现原理,也是依赖于线程池,实际上C#语言,实现对线程的控制,主要就是这两种方式Thread和ThreadPool,其他方式都是进一步封装

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值