多线程之旅(Thread)

      在上篇文章中咱们已经知道了多线程是什么了,那么它到底能够干吗呢?这里特别声明一个前面的委托没看的同窗能够到上上上篇博文查看,由于多线程要常用到委托源码html

1、异步、同步git

      1.同步(在计算的理解老是要你措不及防,同步当线程作完一件事情以后,才会执行后续动做),同步方法慢,只有一个线程执行,异步方法快,由于多个线程一块儿干活,可是二者并非线性增加,当咱们的异步线程占有的资源愈来愈多了,会致使资源可能不够,其次线程过多CPU也是须要管理成本的,因此不是越多越好。web

      2.异步(能够同时执行多个任务,在一样的时间,执行不一样的任务),同步方法卡界面(UI),由于咱们的主线程(UI)忙于计算形成了堵塞了。异步方法不卡界面,计算任务交给了子线程完成。winform中体现的玲玲精致。(你品,你细品),web 能够异步的处理一块儿其余的任务,好比给用户发邮箱(咱们的BS结构的,每次访问都是一个子线程,当咱们的代码写的比较糟糕,是否是加载比较慢呢哈哈)。异步多线程无序,执行的前后无序,执行的时间不肯定,结束也不肯定,因此咱们很难经过执行时间和前后顺序控制,异步的执行顺序。安全

2、初识Thread多线程

属性名称 说明
CurrentContext 获取线程正在其中执行的当前上下文。
CurrentThread 获取当前正在运行的线程。
ExecutionContext 获取一个 ExecutionContext 对象,该对象包含有关当前线程的各类上下文的信息。
IsAlive 获取一个值,该值指示当前线程的执行状态。
IsBackground 获取或设置一个值,该值指示某个线程是否为后台线程。
IsThreadPoolThread 获取一个值,该值指示线程是否属于托管线程池。
ManagedThreadId 获取当前托管线程的惟一标识符。
Name 获取或设置线程的名称。
Priority 获取或设置一个值,该值指示线程的调度优先级。
ThreadState 获取一个值,该值包含当前线程的状态。

Thread 中包括了多个方法来控制线程的建立、挂起、中止、销毁,后面的例子中会常用。异步

方法名称 说明
Abort()     终止本线程。
GetDomain() 返回当前线程正在其中运行的当前域。
GetDomainId() 返回当前线程正在其中运行的当前域Id。
Interrupt() 中断处于 WaitSleepJoin 线程状态的线程。
Join() 已重载。 阻塞调用线程,直到某个线程终止时为止。
Resume() 继续运行已挂起的线程。
Start()   执行本线程。
Suspend() 挂起当前线程,若是当前线程已属于挂起状态则此不起做用
Sleep()   把正在运行的线程挂起一段时间。

      1.Thread是咱们.NET 1.0 给咱们提供的多线程类,能够建立,和控制多线程,Thread类构造函数为接受ThreadStart和ParameterizedThreadStart类型的委托参数,下面有请代码神君。ide

        /// <summary>
        /// 使用Thread 建立多线程
        /// </summary>
        public static void Show()
        {
            //实例化建立线程 无参无返回值
            Thread thread = new Thread(() =>
            {
                Console.WriteLine("我是多线程");
            });
            thread.Start();

            //建立5个线程1
            for (int i = 0; i < 5; i++)
            {
                //这个之因此建立一个k,后面线程不安全会说到
                var k = i;
                //这是一个有参数无返回值多线程
                new Thread(x => Running(Convert.ToInt32(x))).Start(k);
            }
            Console.Read();
        }

 /// <summary>
        /// 一个执行须要长时间的任务
        /// </summary>
        static void Running(int s)
        {
            Console.WriteLine("**********************************");
            Console.WriteLine("执行开始啦" + s);
            Console.WriteLine("获取当前执行的线程ID:" + Thread.CurrentThread.ManagedThreadId.ToString());
            var j = 0;
            for (int i = 0; i < 1000000000; i++)
            {
                j++;
            }
            Console.WriteLine("执行结束啦" + s);
        }
View Code

2、渐入佳境函数

  1.运行上面的代码,能够看到线程的无序性,虽然咱们的0最早开始执行的,可是不是第一个结束的,这个是由于咱们每一个线程执行的时间的不肯定性。这里也要特别说明为何Thread构造函数传递的是ThreadStart和ParameterizedThreadStart类型的委托参数,为何不是Action ,Func,答案就是.NET 1.0的时候尚未Action 、Func。ThreadStart委托是一个无参无返回值上代码中咱们建立了,ParameterizedThreadStart委托是一个有参数无返回值,可是咱们能够看到咱们的参数是一个object类型,是一个不安全的参数(当时泛型也没有出来)固然为了防止这问题,咱们也是想到了方法,那就是咱们能够经过一个泛型类,帮咱们限制参数类型。this

        /// <summary>
        /// 防止参数不安全
        /// </summary>
        public static void Show5()
        {
            //咱们建立一个泛型类,限制咱们的类型
            MyThread<string> mythread = new MyThread<string>("Thread_child");
            //将咱们的方法传递,进去
            Thread th3 = new Thread(mythread.ThreadChild);
            //启动线程
            th3.Start();
        }

        /// <summary>
        /// 建立一个泛型类
        /// </summary>
        /// <typeparam name="T"></typeparam>
        class MyThread<T>
        {
            private T data;
            public MyThread(T data)
            {
                this.data = data;
            }
            public void ThreadChild()
            {
                Console.WriteLine("Child Thread Start! Result:{0}", data);
            }
        }
View Code

  2.咱们在上面还提供了其余的方法,可是这些方法已经不建议使用了,如今已经弃用了,由于咱们没法精确地控制线程的开启与暂停,当咱们将线程挂起的时候,同时也会挂起线程使用的资源,会致使死锁,不建议使用。将线程销毁也不建议    不必定及时/有些动做发出收不回来。(这里我使用的是.net Core 3.1 执行直接报错了哈哈)spa

        /// <summary>
        /// 使用Thread 线程挂起、唤醒线程、销毁,方式是抛异常、取消Abort异常
        /// </summary>
        public static void Show1()
        {
            //建立一个Thread 线程
            Thread thread = new Thread(() =>
            {
                Running();
            });
            //开启线程
            thread.Start();
            //这个是线程挂起
            //thread.Suspend();
            //唤醒线程
            //thread.Resume();
            //上面的两个方法,如今已经弃用了,由于咱们没法精确地控制线程的开启与暂停
            //当咱们将线程挂起的时候,同时也会挂起线程使用的资源,会致使死锁,不建议使用
            try
            {
                //将线程销毁
                //也不建议    不必定及时/有些动做发出收不回来
                thread.Abort();
            }
            catch (Exception)
            {
                //静态方法将线程异常取消继续工做
                Thread.ResetAbort();
            }
            Console.Read();
        }
View Code

   3.线程优先级,固然咱们的线程是一个无序的,也有控制线程执行的权重,可是这个优先级不是绝对的,由于线程的执行顺序仍是看咱们的CPU爸爸的,可是咱们能够利用Priority属性作线程的权重执行,使用也很简单

  /// <summary>
        /// 使用Thread 线程的优先级(可是执行仍是看CPU,能够作优先级,可是不是绝对优先)
        /// </summary>
        public static void Show3()
        {
            //建立一个Thread 线程
            Thread thread = new Thread(() =>
            {
                Running();
            });
            thread.Start();
            //thread.Priority属性能够设置线程的优先级关系
            thread.Priority = ThreadPriority.Highest;
            Console.WriteLine("执行完啦啦啦啦啦啦啦啦啦啦啦拉拉");
            Console.Read();
        }
View Code

  4.前台线程、后台线程(这个字面意思,仍是和咱们的理解是不同的)咱们设置IsBackground控制线程是否(前/后)台线程。默认是前台线程,启动以后必定要完成任务的,阻止进程退出。指定后台线程:随着进程退出。

 3、多线程起飞

  一、异步回调

    1.咱们的Thread没有给我提供异步回调的功能,没办法须要本身造轮子了,咱们能够先想一下回调的需求是什么,需求分析:当咱们的线程任务执行完以后须要以后某些方法。咱们细品一下,咱们要执行完以后,在执行一我的任务,那就是同步执行异步方法了吧。咱们在子线程中怎么同步执行呢?下面的代码就实现了回调功能无论咱们执行多少次回调总会在任务后面执行。

/// <summary>
        /// 异步回调执行
        /// </summary>
        public static void Show6() {
            //建立一个任务委托
            ThreadStart threadStart = () => {
                Console.WriteLine("我是任务");
            };
            //建立一个回调执行的委托
            Action action = () => {
                Console.WriteLine("哈哈,我就是大家的回调方法哈,记得双击么么哒");
                Console.WriteLine("*********************************************");
            };
            ThreadWithCallback(threadStart, action);
            Console.ReadLine();
        }

/// <summary>
        /// 回调封装 无返回值
        /// </summary>
        /// <param name="start"></param>
        /// <param name="callback">回调</param>
        private static void ThreadWithCallback(ThreadStart start, Action callback)
        {
            Thread thread = new Thread(() =>
            {
                start.Invoke();
                callback.Invoke();
            });
            thread.Start();
        }
View Code

   二、返回参数

    1.固然咱们使用线程须要返回参数,可是咱们的Thread没有给咱们提供返回值的委托和方法,这个要莫子搞罗?固然咱们先分析需求,咱们要获取返回值是否是要等线程执行以后呢?好的线程执行咱们可使用Join堵塞线程等它执行完毕,可是咱们要怎么获取返回值呢?对了咱们能够建立一个变量,咱们的线程给变量赋值吗?

/// <summary>
        /// 异步返回值
        /// </summary>
        public static void Show7()
        {
            //建立一个委托
            Func<string> func = () => {
                return "我是返回值";
            };
            //获取执行结果
            Console.WriteLine(ThreadWithReturn(func).Invoke());
            Console.ReadLine();
        }

/// <summary>
        /// 有返回值封装(请根据本案例自行封装回调)
        /// </summary>
        /// <typeparam name="T">返回值类型</typeparam>
        /// <param name="func">须要子线程执行的方法</param>
        /// <returns></returns>
        private static Func<T> ThreadWithReturn<T>(Func<T> func)
        {
            //初始化一个泛型,限制咱们的类型
            T t = default(T);
            ThreadStart newStart = () =>
            {
                //线程给变量赋值
                t = func.Invoke();
            };
            //建立线程
            Thread thread = new Thread(newStart);
            //执行线程
            thread.Start();
            //建立一个委托 无参有返回值,执行委托会发生执行线程等待堵塞
            //当线程执行完以后,也就是说线程已经给变量t赋值了,咱们就返回t
            return new Func<T>(() =>
            {
                thread.Join();
                return t;
            });
        }
View Code

 4、Thread总结

  1.你们是否是以为多线程很酷呢?哈哈我刚刚学的时候也是激动的心颤抖的手。固然文章中咱们介绍了不少API的使用,你们能够动手试试,API的使用是小事,最重要的是咱们的思路,到咱们看到回调封装和返回值封装,咱们都是利用了多线程的一些特性,来完成的这些功能拓展的。咱们宏观的看多线程感受很恐怖,可是在咱们作回调函数的时候是否是感受有一种微观见解,线程执行的内部也是同步的执行哪些方法的。好了今天就写到这里昨天晚上9点多就睡了,早起撸个文章美滋滋。固然多线程还有讲完的,才说道了.NET 1.0哈哈,后续的文章也会写出来。

相关文章
相关标签/搜索