书接上回Unity C#基础之 多线程的前世今生(上) 科普篇,下面为大家介绍从.NET 1.X到4.X版本中多线程的示例,推荐使用.NET 3.X版本
注意:打印提示中信息很重要,反馈出线程ID和顺序,便于理解欢迎大家留言交流
- 【2018.06.20更新】Task.Delay(millisecond) .ContinueWith(t=>{DoSomething();}) //task延迟执行,不卡主线程
示例工程下载
.NET 1.X版本
- Thread的基本使用
- 基于Thread封装支持回调
- 基于Thread封装支持回调带返回值
Thread的基本使用
    /// 
    /// 多线程1.0  1.1
    ///  
    private void ThreadsOnClick()
    {
        System.Diagnostics.Stopwatch watch = new System.Diagnostics.Stopwatch();
        watch.Start();
        Debug.Log($"ThreadsOnClick Start {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}");
        {
            Action act = new Action(() => this.DoSomethingLong("ThreadsOnClick"));
            ThreadStart threadStart = new ThreadStart(() =>
            {
                Thread.Sleep(5000);
                this.DoSomethingLong("ThreadsOnClick");
            });
            Thread thread = new Thread(threadStart)
            {
                IsBackground = true//变成后台线程
            };//Action和ThreadStart 约束一致,但是不同类型
            thread.Start();//默认是前台线程,UI线程退出后,还会继续执行完;后台线程就直接退出了
            //thread.Suspend();
            //thread.Resume();
            //thread.Join();//做等待
            //thread.Abort();
            //while (thread.ThreadState != System.Threading.ThreadState.Running)
            //{
            //}
        }
        watch.Stop();
        Debug.Log($"ThreadsOnClick End   {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}  耗时{watch.ElapsedMilliseconds}ms");
    }打印结果如下

基于Thread封装支持回调
    private void ThreadsOnClick01()
    {
        System.Diagnostics.Stopwatch watch = new System.Diagnostics.Stopwatch();
        watch.Start();
        Debug.Log($"ThreadsOnClick Start {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}");
        this.ThreadWithCallback(() =>
            {
                Thread.Sleep(2000);
                Debug.Log($"这里是ThreadWithCallback 线程ID:{Thread.CurrentThread.ManagedThreadId.ToString("00")}");
            }
            , () =>
             {
                 Thread.Sleep(2000);
                 Debug.Log($"这里是ThreadWithCallback回调 线程ID:{Thread.CurrentThread.ManagedThreadId.ToString("00")}");
             });
        watch.Stop();
        Debug.Log($"ThreadsOnClick End   {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}  耗时{watch.ElapsedMilliseconds}ms");
    }
    /// 
    /// 基于Thread封装支持回调
    /// BeginInvoke的回调
    ///  
    /// 
    /// 
    private void ThreadWithCallback(ThreadStart threadStart, Action callback)
    {
        ThreadStart startNew = new ThreadStart(
            () =>
            {
                threadStart();
                callback.Invoke();
            });
        Thread thread = new Thread(startNew);
        thread.Start();
    }打印结果如下

基于Thread封装支持回调带返回值
    private void ThreadsOnClick02()
    {
        System.Diagnostics.Stopwatch watch = new System.Diagnostics.Stopwatch();
        watch.Start();
        Debug.Log($"ThreadsOnClick Start {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}");
        Func func = this.ThreadWithReturn(() =>//begininvoke
        {
            Thread.Sleep(2000);
            Debug.Log($"这里是ThreadStart {Thread.CurrentThread.ManagedThreadId.ToString("00")}");
            return 12345;
        });
        int iResult = func.Invoke();//相当于异步endinvoke
        watch.Stop();
        Debug.Log($"ThreadsOnClick End   {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}  耗时{watch.ElapsedMilliseconds}ms");
    }
    /// 
    /// 基于Thread封装支持回调带返回值
    ///  
    ///  ThreadWithReturn(Func funcT)
    {
        T t = default(T);
        ThreadStart startNew = new ThreadStart(
            () =>
            {
                t = funcT.Invoke();
            });
        Thread thread = new Thread(startNew);
        thread.Start();
        return new Func(() =>
        {
            thread.Join();
            return t;
        });
    }     打印结果如下

.NET 2.X版本
- ThreadPool.QueueUserWorkItem
- ManualResetEvent
- ThreadPool.SetMaxThreads等参数设置
ThreadPool.QueueUserWorkItem
    /// 
    /// 线程池 2.0   封装
    /// 1 去掉各种 api 避免滥用,降低复杂度
    /// 2 池化:1减少创建/销毁的成本 2 限制最大线程数量
    ///  
    private void ThreadPoolOnClick()
    {
        System.Diagnostics.Stopwatch watch = new System.Diagnostics.Stopwatch();
        watch.Start();
        Debug.Log($"ThreadPoolOnClick Start {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}");
        {
            ThreadPool.QueueUserWorkItem(
                o =>
                {
                    new Action(() =>
                    {
                        this.DoSomethingLong("Action:ThreadPoolOnClick");
                        //回调  就包一层,放在这里
                    }).Invoke();
                    Debug.Log("1234356786954");
                });
            ManualResetEvent mre = new ManualResetEvent(false); //相当于WaitOne() 、Join()一种等待状态,会阻碍主线程 状态变为True开始执行下一行Code
            ThreadPool.QueueUserWorkItem(o =>
            {
                this.DoSomethingLong("ThreadPoolOnClick");
                Debug.Log(o.ToString());
                mre.Set();
            }, "QueueUserWorkItem.State");
            Debug.Log("before WaitOne");
            mre.WaitOne();
            Debug.Log("after WaitOne");
        }
        watch.Stop();
        Debug.Log($"ThreadPoolOnClick End   {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}  耗时{watch.ElapsedMilliseconds}ms");
    }打印结果如下

ManualResetEvent
    private void ThreadPoolOnClick01()
    {
        #region ManualResetEvent
        //没有需求。就别等待 阻塞线程
        {
            ManualResetEvent mre = new ManualResetEvent(false);//false 关闭
            new Action(() =>
            {
                Thread.Sleep(1000);
                Debug.Log($"委托的异步调用01  线程ID:{Thread.CurrentThread.ManagedThreadId.ToString("00")} 时间点: {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}");
                mre.Set();//打开
            }).BeginInvoke(null, null);
            mre.WaitOne();
            Debug.Log(" mre.WaitOne()状态为打开");
            mre.Reset();//关闭
            new Action(() =>
            {
                Thread.Sleep(1000);
                Debug.Log($"委托的异步调用02  线程ID:{Thread.CurrentThread.ManagedThreadId.ToString("00")} 时间点: {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}");
                mre.Set();//打开
            }).BeginInvoke(null, null);
            mre.WaitOne();
            Debug.Log("结束");
        }
        #endregion
    }打印结果如下

ThreadPool.SetMaxThreads等参数设置
    public void ThreadPoolSetParameter()
    {
        #region PoolSet
        ThreadPool.SetMaxThreads(8, 8);//最小也是核数
        ThreadPool.SetMinThreads(8, 8);
        int workerThreads = 0;
        int ioThreads = 0;
        ThreadPool.GetMaxThreads(out workerThreads, out ioThreads);
        Debug.Log(String.Format("Max worker threads: {0};    Max I/O threads: {1}", workerThreads, ioThreads));
        ThreadPool.GetMinThreads(out workerThreads, out ioThreads);
        Debug.Log(String.Format("Min worker threads: {0};    Min I/O threads: {1}", workerThreads, ioThreads));
        ThreadPool.GetAvailableThreads(out workerThreads, out ioThreads);
        Debug.Log(String.Format("Available worker threads: {0};    Available I/O threads: {1}", workerThreads, ioThreads));
        #endregion
    }打印结果如下

.NET 3.X版本 (推荐)
- Task.Run&&TaskFactory 和状态等待
- ContinueWith
- Parallel - Task.Run&&TaskFactory 和状态等待- ///- /// Task 3.0 /// 使用的是线程池的线程 全部是后台线程 /// API很强大 /// /// 多线程:业务是可以并发执行 /// /// 千万不要在Task里面去启动Task /// private void TaskOnClick() { System.Diagnostics.Stopwatch watch = new System.Diagnostics.Stopwatch(); watch.Start(); Debug.Log($"****************TaskOnClick Start {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************"); Task t1 = Task.Run(new Action(() => { this.DoSomethingLong("Task.Run:TaskOnClick"); })); TaskFactory taskFactory = Task.Factory;//等同于 new TaskFactory(); List- taskList = new List - (); taskList.Add(taskFactory.StartNew(() => this.DoSomethingLong("btnTask_Click_001"))); taskList.Add(taskFactory.StartNew(() => this.DoSomethingLong("btnTask_Click_002"))); taskList.Add(taskFactory.StartNew(() => this.DoSomethingLong("btnTask_Click_003"))); //回调 //taskList.Add(taskFactory.ContinueWhenAny(taskList.ToArray(), t => Debug.Log($"这里是ContinueWhenAny {Thread.CurrentThread.ManagedThreadId.ToString("00")}")));//ContinueWhenAny其中任何一个线程完成回调 异步检测完成不卡界面 //taskList.Add(taskFactory.ContinueWhenAll(taskList.ToArray(), tList => Debug.Log($"这里是ContinueWhenAll {Thread.CurrentThread.ManagedThreadId.ToString("00")}")));//ContinueWhenAll所有线程完成回调 异步检测完成不卡界面 taskFactory.ContinueWhenAny(taskList.ToArray(), t => Debug.Log($"这里是ContinueWhenAny {Thread.CurrentThread.ManagedThreadId.ToString("00")}"));//ContinueWhenAny其中任何一个线程完成回调 异步检测完成不卡界面 taskFactory.ContinueWhenAll(taskList.ToArray(), tList => Debug.Log($"这里是ContinueWhenAll {Thread.CurrentThread.ManagedThreadId.ToString("00")}"));//ContinueWhenAll所有线程完成回调 异步检测完成不卡界面 //需要多线程加快速度 同时又要求某个完成后,才能返回 //多业务操作 希望并发,,但是某个完成后,才能返回 //Task.WaitAny(taskList.ToArray());//卡界面 //Debug.Log("某个任务都完成,才会执行"); //需要多线程加快速度 同时又要求全部完成后,才能返回 //多业务操作 希望并发,,但是全部完成后,才能返回 //Task.WaitAll(taskList.ToArray());//卡界面 //Debug.Log("全部任务都完成,才会执行"); watch.Stop(); Debug.Log($"****************TaskOnClick End {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} 耗时{watch.ElapsedMilliseconds}ms***************"); } - 打印结果如下 
ContinueWith
    private void TaskOnClick01()
    {
        System.Diagnostics.Stopwatch watch = new System.Diagnostics.Stopwatch();
        watch.Start();
        Debug.Log($"****************TaskOnClick01 Start {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
        TaskFactory taskFactory = Task.Factory;//等同于 new TaskFactory();
        Task task = taskFactory.StartNew(t => this.DoSomethingLong("btnTask_Click_005"), "菜鸟海澜").ContinueWith(t => Debug.Log($"这里是{t.AsyncState}的回调"));
        Task intTask = taskFactory.StartNew(() => 123);
        int iResult = intTask.Result;
        Debug.Log($"线程结果{iResult}");
        watch.Stop();
        Debug.Log($"****************TaskOnClick01 End   {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}  耗时{watch.ElapsedMilliseconds}ms***************");
    } 打印结果如下

.NET 4.X版本
- Parallel.Invoke
- Parallel.For
- Parallel.ForEach
- ParallelOptions&&ParallelLoopState
Parallel.Invoke
    /// 
    /// Parallel
    ///  
    private void ParallelOnClick()
    {
        System.Diagnostics.Stopwatch watch = new System.Diagnostics.Stopwatch();
        watch.Start();
        Debug.Log($"ParallelOnClick Start {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}");
        //跟task很像,==task+waitall 卡界面 启动多个线程计算   主线程也参与计算,就是节约了一个线程
        Parallel.Invoke(() => this.DoSomethingLong("btnParallel_Click_001"),
            () => this.DoSomethingLong("btnParallel_Click_002"),
            () => this.DoSomethingLong("btnParallel_Click_003"),
            () => this.DoSomethingLong("btnParallel_Click_004"),
            () => this.DoSomethingLong("btnParallel_Click_005"));
        watch.Stop();
        Debug.Log($"ParallelOnClick End   {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}  耗时{watch.ElapsedMilliseconds}ms");
    }打印结果如下,主线程参与计算

Parallel.For
    private void ParallelOnClick01()
    {
        System.Diagnostics.Stopwatch watch = new System.Diagnostics.Stopwatch();
        watch.Start();
        Debug.Log($"ParallelOnClick01 Start {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}");
        Parallel.For(0, 5, t =>
          {
              this.DoSomethingLong($"btnParallel_Click_00{t}");
          });
        watch.Stop();
        Debug.Log($"ParallelOnClick01 End   {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}  耗时{watch.ElapsedMilliseconds}ms");
    }打印结果如下

Parallel.ForEach
    private void ParallelOnClick02()
    {
        System.Diagnostics.Stopwatch watch = new System.Diagnostics.Stopwatch();
        watch.Start();
        Debug.Log($"ParallelOnClick02 Start {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}");
        Parallel.ForEach(new int[] { 0, 1, 2, 3, 4 }, t =>
                {
                    this.DoSomethingLong($"btnParallel_Click_00{t}");
                });
        watch.Stop();
        Debug.Log($"ParallelOnClick02 End   {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}  耗时{watch.ElapsedMilliseconds}ms");
    }打印结果如下

ParallelOptions&&ParallelLoopState
    private void ParallelOnClick03()
    {
        System.Diagnostics.Stopwatch watch = new System.Diagnostics.Stopwatch();
        watch.Start();
        Debug.Log($"ParallelOnClick03 Start {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}");
        ParallelOptions options = new ParallelOptions()
        {
            MaxDegreeOfParallelism = 3//最大并行数量(线程数)
        };
        //Parallel.ForEach(new int[] { 0, 1, 2, 3, 4 }, options, t =>
        //{
        //    this.DoSomethingLong($"btnParallel_Click_00{t}");
        //});
        Parallel.ForEach(new int[] { 0, 1, 2, 3, 4 }, options, (t, state) =>
        {
            this.DoSomethingLong($"btnParallel_Click_00{t}");
            if (t == 4)
            {
                /*
                 * 调用Stop方法表明,尚未启动的循环的任何迭代都不需要运行。它有效地取消了循环的任何额外迭代。但是,它并没有停止任何已经开始执行的迭代。
                 * 
                 */
                state.Stop();//没有运行的线程会取消结束
                Debug.Log($"Stop全部线程 线程ID:{Thread.CurrentThread.ManagedThreadId.ToString("00")}");
                return;
            }
            if (state.IsStopped)
            {
                Debug.Log($"已经被其他线程调用Stop 线程ID:{Thread.CurrentThread.ManagedThreadId.ToString("00")}");
                return;
            }
            /*
             * //break 与stop同时使用可能触发异常:InvalidOperationException: Stop was called after Break was called
             * https://msdn.microsoft.com/en-us/library/system.threading.tasks.parallelloopstate.break(v=vs.100).aspx
             * https://msdn.microsoft.com/en-us/library/system.threading.tasks.parallelloopstate.stop.aspx
             */
            // if (t == 2) 
            //{
            //    Debug.Log($"Break线程 线程ID:{Thread.CurrentThread.ManagedThreadId.ToString("00")}");
            //    state.Break();//停止当前的
            //    return;
            //}
        });
        watch.Stop();
        Debug.Log($"ParallelOnClick03 End   {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}  耗时{watch.ElapsedMilliseconds}ms");
    }打印结果如下

