(精华)2020年8月12日 C#基础知识点 异步专题(发展史,多线程,多进程,协程,单线程非阻塞)

(精华)2020年8月12日 C#基础知识点 异步专题(发展史,多线程,多进程,协程,单线程非阻塞)

一:引言

/// .NetFramework1.0就有多线程!
/// 进程:计算机概念,程序运行在服务器占据的全部计算机的资源 
/// 线程:计算机概念,是进程在相应操作时候的一个最小单元,也包括cpu/硬盘/内存  虚拟概念 
/// 进程和线程:包含关系,线程是属于某一个进程的,如果一个进程销毁,线程也就不会存在。 
/// 句柄:描述程序中的某一个最小单元,是一个long数字,操作系统通过这个数字识别应用程序。 
/// 多线程:计算概念,就是某一个进程中,多个线程同时运行;
///  
/// C#中的多线程: 
/// Thread类是C#语言对线程对象一个封装;
/// 
/// 为什么可以多线程呢?
/// 
/// 1、Cpu有多个核;可以并行计算;
///    双核四线:这里的线程是模拟核;
/// 
/// 2、cpu分片:某1s的处理能切分成1000份,操作系统调度去相应不同的任务;
///   从宏观角度来说:感觉就有多个任务在并发执行;
///   从微观角度来说:一个物理cpu不能在某一刻为某一个任务服务
///   
/// 同步异步:
///     同步方法:发起调用,只有在调用的方法完成以后,才能继续执行一下一行代码,按照顺序执行;
///     诚心请吃饭,我请你吃饭,你说你现在需要忙一会儿,我等你,等你忙完了以后,咱们一起去吃饭。
///     
///     异步方法:发起调用,不等待完成,直接进入下一行代码的执行,启动一个新的线程来完成计算
///     
///     客气一下请人吃饭:我请你吃饭,你说你现在需要忙一会儿,我就不等你了,我自己先去吃饭了,你忙完以后,自己去吃饭。

二:同步和异步的比较

#region Sync
        /// <summary>
        /// 同步方法
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnSync_Click(object sender, EventArgs e)
        {<!-- -->
            Console.WriteLine($"****************btnSync_Click Start {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
            int l = 3;
            int m = 4;
            int n = l + m;
            for (int i = 0; i < 5; i++)
            {<!-- -->
                string name = string.Format($"btnSync_Click_{i}");
                this.DoSomethingLong(name);
            }
            Console.WriteLine($"****************btnSync_Click   End {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");

        }
        #endregion

        #region Async
        /// <summary>  
        /// 异步方法:
        ///  1、同步方法卡界面:主线线程(UI线程)忙于计算,无暇他顾 
        ///     异步方法不卡界面:因为异步方法是新启动一个线程去完后计算,主线程闲置
        ///     改善用户体验,winform程序点击某一个按钮,不会卡死界面;
        ///     发短信,发邮件可以交给一个子线程去完成
        ///     
        ///  2、同步方法执行慢:只有一个线程完成计算
        ///     异步方法执行快:多个线程去完成计算
        ///     10000ms   3000ms   快了三倍多
        ///     20000ms   15000ms  cpu密集型计算
        ///     资源换性能
        ///     
        ///  3、同步方法有序执行,异步多线程无顺序
        ///     启动无序,线程资源是向操作系统申请的,操作系统有自己的调度策略,所以启动是随机的;
        ///     以上两点得出: 结束也是没有顺序
        ///    
        /// 如果需要控制顺序呢?怎么实现? 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnAsync_Click(object sender, EventArgs e)
        {<!-- -->
            Console.WriteLine($"****************btnAsync_Click Start {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");

            Action<string> action = this.DoSomethingLong;

            for (int i = 0; i < 5; i++)
            {<!-- -->
                action.BeginInvoke("btnAsync_Click", null, null);
            }

            Console.WriteLine($"****************btnAsync_Click End   {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
        }
        #endregion


        #region Private Method
        /// <summary>
        /// 一个比较耗时耗资源的私有方法
        /// </summary>
        /// <param name="name"></param>
        private void DoSomethingLong(string name)
        {<!-- -->
            Console.WriteLine($"****************DoSomethingLong Start  {name}  {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
            long lResult = 0;
            Thread.Sleep(2000);//线程等待
            Console.WriteLine($"****************DoSomethingLong   End  {name}  {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} {lResult}***************");
        }
        #endregion

        private void btnAsyncAdvanced_Click(object sender, EventArgs e)
        {<!-- -->
             Console.WriteLine($"****************btnAsyncAdvanced_Click Start   {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");

            IAsyncResult asyncResult = null;
            AsyncCallback callback = ar =>
            {<!-- -->

                Thread.Sleep(5000);
                Console.WriteLine($"这里是beginInvoke的第三个参数{ar.AsyncState}");
                Console.WriteLine(object.ReferenceEquals(ar, asyncResult));
                Console.WriteLine($"{Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy - MM - dd HH: mm:ss.fff")}");
                Console.WriteLine("计算结束");
            };
            {<!-- -->
                //1、回调
                Action<string> action = this.DoSomethingLong;
                //把自定义的参数传入到回调函数中去
                asyncResult = action.BeginInvoke("btnAsyncAdvanced_Click", callback, "八万公里");
                //2、IsCompleted 完成等待 
                {<!-- -->
                    int i = 0;
                while (!asyncResult.IsCompleted)
                {<!-- -->
                    if (i < 9)
                    {<!-- -->
                        Console.WriteLine($"正在玩命为你加载中。。。已经完成{++i * 10}%");
                    }
                    else
                    {<!-- -->
                        Console.WriteLine($"正在玩命为你加载中。。。已经完成99.9999%");
                    }
                    Thread.Sleep(200);
                }

                Console.WriteLine("加载完成。。。");
            }

                //以上两种都是为了等待任务的完成;
                //3、WaitOne等待
                asyncResult.AsyncWaitHandle.WaitOne();//一直等待任务完成
                asyncResult.AsyncWaitHandle.WaitOne(-1);//一直等待任务完成
                asyncResult.AsyncWaitHandle.WaitOne(3000);//最多等待3000ms,如果超时了,就不等待了 

                {<!-- -->
                //4、EndInvoke也可以等待,可以获取委托返回值
                Func<int> func = () =>
                {<!-- -->
                    //Thread.Sleep(5000);
                    return DateTime.Now.Year;
                };
                func.Invoke();
                IAsyncResult asyncResult1 = func.BeginInvoke(ar =>
                  {<!-- -->
                      func.EndInvoke(ar);
                  }, null);

                int iResult = func.EndInvoke(asyncResult1);
                Console.WriteLine(iResult);

            }
            Console.WriteLine($"****************btnAsyncAdvanced_Click   End   {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}  ***************");
        }

三:.NetFramework1.0(Thread)

{<!-- -->
    ParameterizedThreadStart threadStart = ar =>
    {<!-- -->
        this.DoSomethingLong("btnThread_Click");
        Console.WriteLine($"****************btnThread_Click   End   {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}  ***************");
    };
    Thread thread = new Thread(threadStart);
    thread.Start(); //开启一个新线程
}
{<!-- -->
     ThreadStart threadStart = () =>
     {<!-- -->
         Thread.Sleep(5000);
         this.DoSomethingLong("btnThread_Click");
         Console.WriteLine($"****************btnThread_Click   End   {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}  ***************");
     };
     Thread thread = new Thread(threadStart);
     thread.Start(); //开启一个新线程  
     thread.Suspend();// 暂停线程
     thread.Resume();//恢复  无法实时的去暂停或者恢复线程 
     thread.Abort();//终结线程
     Thread.ResetAbort();//都会有延时


     //如果我们需要等待;
     //1、等待
     while (thread.ThreadState != ThreadState.Stopped)
     {<!-- -->
         Thread.Sleep(200);
     }
     //2、Join等待
     thread.Join();//可以限时等待
     thread.Join(2000); //可以限时等待

     thread.Priority = ThreadPriority.Highest;//设置线程优先级,只是增加他的优先概率;并不能一定的

     thread.IsBackground = true;//为后台线程  进程结束,线程结束了
     thread.IsBackground = false; //前台线程   进程结束后,任务执行完毕以后,线程才结束 
     {<!-- -->
         ThreadStart threadStart1 = () =>
         {<!-- -->
             Console.WriteLine($"****************btnThread_Click   End   {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}  ***************");
             Thread.Sleep(3000);
         };

         Action action = () =>
         {<!-- -->
             Console.WriteLine($"****************btnThread_Click   End   {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}  ***************");

             Thread.Sleep(3000);
         };
         this.ThreadWithCallBack(threadStart1, action); 
     }
     {<!-- -->
         //Thread开启一个新的线程执行任务,如何获取返回结果:
         Func<int> func = () =>
         {<!-- -->
             Thread.Sleep(5000);
             return DateTime.Now.Year;
         };
         Func<int> FuncResult = this.ThreadWithReturn(func);
         int iResult = FuncResult.Invoke();//如果需要得到执行结果,是必须要等待的
     }
 }
 /// <summary>
 /// 开启线程执行委托函数
 /// </summary>
 /// <param name="threadStart"></param>
 /// <param name="actionCallback"></param>
 private void ThreadWithCallBack(ThreadStart threadStart, Action actionCallback)
 {<!-- -->
     ThreadStart threadStart1 = new ThreadStart(() =>
     {<!-- -->
         threadStart.Invoke();
         actionCallback.Invoke();
     });
     Thread thread = new Thread(threadStart1);
     thread.Start();
 }


 /// <summary>
 /// 既要不卡界面,又需要返回结果  
 /// </summary>
 /// <typeparam name="T"></typeparam>
 /// <param name="func"></param>
 /// <returns></returns>
 private Func<T> ThreadWithReturn<T>(Func<T> func)
 {<!-- -->
     T t = default(T);
     ThreadStart threadStart = new ThreadStart(() =>
     {<!-- -->
         t = func.Invoke();
     });
     Thread thread = new Thread(threadStart);
     thread.Start();
     return new Func<T>(() =>
     {<!-- -->
         thread.Join();
         return t;
     });
 }

四:.NetFramework2.0(ThreadPool)

/// <summary>
/// 线程池
/// .NetFramework2.0 
/// 在Thread中对线程的管理需要我们自己去从操作,在不断的开启线程和销毁中,存在很大的开销,为了让线程可以反复的使用,出现了池化思想!
/// 可以节省资源,线程池还可以控制线程总数量,防止滥用!
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnThreadPool_Click(object sender, EventArgs e)
{<!-- -->
    Console.WriteLine($"****************btnThreadPool_Click Start   {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
    {<!-- -->
        ThreadPool.QueueUserWorkItem(o =>
        {<!-- -->
            Console.WriteLine($"**************** {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
            this.DoSomethingLong("ThreadPool.QueueUserWorkItem1");//开启了一个线程
        }); 
    }
    {<!-- -->
        ThreadPool.QueueUserWorkItem(o =>
        {<!-- -->
            Console.WriteLine($"第二个参数:{o}");
            this.DoSomethingLong("ThreadPool.QueueUserWorkItem1");//开启了一个线程
        }, "Hyl");
    }
    {<!-- -->
        ThreadPool.GetMaxThreads(out int maxWorkerThreads, out int maxCompletionPortThreads);
        ThreadPool.GetMinThreads(out int minWorkerThreads, out int minCompletionPortThreads);

        Console.WriteLine($"当前电脑最大workerThreads={maxWorkerThreads},最大的completionPortThreads={maxCompletionPortThreads}");
        Console.WriteLine($"当前电脑最小workerThreads={minWorkerThreads},最小的completionPortThreads={minCompletionPortThreads}");

        Console.WriteLine("设置线程数量之后:");
        //设置线程数量是全局,线程池是全局,Task,async/awit 都是来自于线程,老师不建议大家随便设置! 
        ThreadPool.SetMaxThreads(2, 2); //这里在设置的时候,数量不能低于本计算机的 核数
        ThreadPool.SetMinThreads(2, 2);

        ThreadPool.GetMaxThreads(out int maxWorkerThreads1, out int maxCompletionPortThreads1);
        ThreadPool.GetMinThreads(out int minWorkerThreads1, out int minCompletionPortThreads1);

        Console.WriteLine($"当前电脑最大workerThreads={maxWorkerThreads1},最大的completionPortThreads={maxCompletionPortThreads1}");
        Console.WriteLine($"当前电脑最小workerThreads={minWorkerThreads1},最小的completionPortThreads={minCompletionPortThreads1}");
    }
    {<!-- -->
        //线程等待:
        //在线程池中,有一个开关式 
        ManualResetEvent manualResetEvent = new ManualResetEvent(false); //开关关闭 
        ThreadPool.QueueUserWorkItem(o =>
        {<!-- -->
            this.DoSomethingLong("btnThreadPool_Click1");
            Thread.Sleep(3000);
            manualResetEvent.Set();// 开关打开
        });
        manualResetEvent.WaitOne();//执行到这儿来的时候,我就等你给我发信号
        Console.WriteLine("计算完成");
    }
    {<!-- -->
        ThreadPool.SetMaxThreads(8, 8);//设置了最大的线程数量
        ManualResetEvent manualResetEvent = new ManualResetEvent(false); //开关关闭  
        for (int i = 0; i < 10; i++)
        {<!-- -->
            int k = i;
            ThreadPool.QueueUserWorkItem(t =>
               {<!-- -->
                   Console.WriteLine($"{Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}");
                   if (k == 9)
                   {<!-- -->
                       manualResetEvent.Set();
                   }
                   else
                   {<!-- -->
                       manualResetEvent.WaitOne();
                   }

               });

        }
        if (manualResetEvent.WaitOne())
        {<!-- -->
            Console.WriteLine("执行成功!");
        }
    }
    Console.WriteLine($"****************btnThreadPool_Click   End   {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}  ***************");
}

五:.NetFramework3.0(Task)

/// <summary>
/// Task 是.NetFramework3.0出现的。提供了非常多的Api
/// Task里面的线程是来自于线程池!
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btntask_Click(object sender, EventArgs e)
{<!-- -->
    Console.WriteLine($"****************btnThreadPool_Click Start   {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
    {<!-- -->
        Task task = new Task(() =>
        {<!-- -->
            this.DoSomethingLong("btntask_Click1");
        });
        task.Start();//开启了一个新的线程
    }
    {<!-- -->
        Task.Run(() =>
        {<!-- -->
            this.DoSomethingLong("btntask_Click2");
        });
    }
    {<!-- -->
        //TaskFactory taskFactory = new TaskFactory();
        TaskFactory taskFactory = Task.Factory;
        taskFactory.StartNew(() =>
        {<!-- -->
            this.DoSomethingLong("btntask_Click3");
        });
    }
    {<!-- -->
        Thread thread = new Thread(() => {<!-- --> Console.WriteLine("开启了一个新线程"); });
        thread.Start();
        ThreadPool.QueueUserWorkItem(o => {<!-- --> Console.WriteLine("开启了一个新线程"); });
    }
    {<!-- -->
        {<!-- -->
            Stopwatch stopwatch = new Stopwatch();
            stopwatch.Start();
            Thread.Sleep(2000);
            stopwatch.Stop();
            Console.WriteLine($"{stopwatch.ElapsedMilliseconds}");
        }
        {<!-- -->
            // Task.Delay 出现于4.5版本
            Stopwatch stopwatch = new Stopwatch();
            stopwatch.Start();
            Task task = Task.Delay(2000).ContinueWith(t =>  //任务在2000ms 以后执行
            {<!-- -->
                stopwatch.Stop();
                Console.WriteLine($"{stopwatch.ElapsedMilliseconds}");
                Console.WriteLine("回调已完成");
            });
        }
    }
    {<!-- -->
        //可以并发执行的时候就可以使用多线程

        List<Task> tasksList = new List<Task>();

        TaskFactory taskFactory = new TaskFactory();

        tasksList.Add(taskFactory.StartNew(() => {<!-- --> this.Coding("001", "系统管理"); }));
        tasksList.Add(taskFactory.StartNew(() => {<!-- --> this.Coding("002", "部门管理"); }));
        tasksList.Add(taskFactory.StartNew(() => {<!-- --> this.Coding("003", "客户管理"); }));
        tasksList.Add(taskFactory.StartNew(() => {<!-- --> this.Coding("004", "接口管理"); }));
        tasksList.Add(taskFactory.StartNew(() => {<!-- --> this.Coding("005", "写Api"); }));

        //如果有一个同学完成了某一个模块,老师就需要准备环境!
        // 等待某一个线程执行完毕以后 继续往后执行
        Task.WaitAny(tasksList.ToArray());

        Task.WaitAll(tasksList.ToArray()); //阻塞主线程
    }

    Console.WriteLine($"****************btnThreadPool_Click   End   {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}  ***************");
}

六:.NetFramework4.5(Parallel )

/// <summary>
/// Parallel 对Task进一步进行了封装 .Netframework 4.5版本出来
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnParallel_Click(object sender, EventArgs e)
{<!-- -->
    Console.WriteLine($"**************** btnParallel_Click  Start  {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}  ***************");
    {<!-- -->

        //多线程中控制执行顺序,一直是一个难题,使用Parallel可以控制线程的执行顺序。
        //Parallel并发执行了五个委托,开启了新线程,主线程参与计算,界面会阻塞
        // Task WaitAll  + 主线程
        Parallel.Invoke(() => {<!-- --> this.DoSomethingLong("btnParallel_Click_1"); },
            () => {<!-- --> this.DoSomethingLong("btnParallel_Click_2"); },
            () => {<!-- --> this.DoSomethingLong("btnParallel_Click_3"); },
            () => {<!-- --> this.DoSomethingLong("btnParallel_Click_4"); },
            () => {<!-- --> this.DoSomethingLong("btnParallel_Click_5"); });
    }
    {<!-- -->
        ParallelOptions parallelOptions = new ParallelOptions();
        parallelOptions.MaxDegreeOfParallelism = 2;//最大并行数
        Parallel.For(0, 10, parallelOptions, t => this.DoSomethingLong($"btnParallel_Click_{t}"));
    }
    {<!-- -->
        控制线程数量
        ParallelOptions parallelOptions = new ParallelOptions();
        parallelOptions.MaxDegreeOfParallelism = 2;  //控制线程的最大数量
        //控制执行数量
        Parallel.ForEach(new int[] {<!-- --> 12, 13, 14, 15, 16, 17 }, parallelOptions, t => this.DoSomethingLong($"btnParallel_Click_{t}"));
    }
    Console.WriteLine($"**************** btnParallel_Click  End  {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}  ***************");
}

七:多线程异常处理和异常取消

/// <summary>
/// 多线程异常处理
/// 线程取消
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnThreadCore_Click(object sender, EventArgs e)
{<!-- -->
    Console.WriteLine($"**************** btnThreadCore_Click  Start  {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}  ***************");

    //多线程中如果某一个线程异常了,就会终结当前线程;对其他的线程是没有影响的;

    //多线程中的异常去哪儿了?  
    //被吞掉了
    #region 多线程异常处理
    {<!-- -->
        Thread thread = new Thread('执行方法');
        thread.Abort();
        try
        {<!-- -->
            List<Task> taskList = new List<Task>();
            for (int i = 0; i < 100; i++)
            {<!-- -->
                string name = $"btnThreadCore_Click_{i}";
                int k = i;
                taskList.Add(Task.Run(() =>
                {<!-- -->
                    if (k == 5)
                    {<!-- -->
                        throw new Exception($"{name} 异常了");
                    }
                    else if (k == 6)
                    {<!-- -->
                        throw new Exception($"{name} 异常了");
                    }
                    else if (k == 10)
                    {<!-- -->
                        throw new Exception($"{name} 异常了");
                    }
                    Console.WriteLine($"this is {name} Ok!");
                }));
            };
            Task.WaitAll(taskList.ToArray());
        }
        catch (AggregateException aex)  //可以有多个Catch 在匹配异常类型的时候,先具体,然后在寻找父类
        {<!-- -->
            foreach (var exception in aex.InnerExceptions)
            {<!-- -->
                Console.WriteLine(exception.Message);
            }
        }
        catch (Exception ex)
        {<!-- -->
            Console.WriteLine(ex.Message);
            throw;
        }
        //在实际的工作中,往往有很多场景是如果发生异常之后,其他的线程就需要取消,不再继续往下执行;问题就是如何取消线程;
    }
    #endregion
    #region 线程取消
    {<!-- -->
        //全局变量  
        //bool IsOk = true;
        CancellationTokenSource cts = new CancellationTokenSource();// 通知式的
        try
        {<!-- --> 
            List<Task> taskList = new List<Task>();
            for (int i = 0; i < 100; i++)
            {<!-- -->
                string name = $"btnThreadCore_Click_{i}";
                int k = i;
                taskList.Add(Task.Run(() =>
                {<!-- -->
                    if (k == 5)
                    {<!-- --> 
                        throw new Exception($"{name} 异常了");
                    }
                    if (!cts.IsCancellationRequested)//是否取消
                    {<!-- -->
                        Console.WriteLine($"this is {name} Ok!");
                    }
                    else
                    {<!-- -->
                        Console.WriteLine($"this is {name} Stop!");
                    } 
                }));
            };
            Task.WaitAll(taskList.ToArray());
        }
        catch (AggregateException aex)  //可以有多个Catch 在匹配异常类型的时候,先具体,然后在寻找父类
        {<!-- -->
            cts.Cancel(); //执行该方法以后,IsCancellationRequested会被指定为false
            foreach (var exception in aex.InnerExceptions)
            {<!-- -->
                Console.WriteLine(exception.Message);
            }
        }
        catch (Exception ex)
        {<!-- -->
            Console.WriteLine(ex.Message);
            throw;
        } 
    }
    #endregion
    Console.WriteLine($"**************** btnThreadCore_Click  End  {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}  ***************");
}

八:多线程安全问题

private static readonly object Obj_Lock = new object();
//1 Lock;不能锁null,不建议大家锁String; 锁 this
//使用锁:private static readonly object Obj_Lock = new object()
//锁的作用:排他
//2  线程安全集合 
// System.Collections.Concurrent.ConcurrentStack 基于线程安全  
//3 数据分拆,避免多个线程操作同一堆数据,安全又高效率

//保证线程安全不会使用同一个变量
for (int i = 0; i < 100000; i++)
{<!-- -->
    this.NumOne += 1;
}

for (int i = 0; i < 100000; i++)
{<!-- -->
    Task.Run(() =>
    {<!-- -->
        try
        {<!-- -->
        lock (Obj_Lock )//可以 避免多线程并发,如果锁住以后,其实这里跟单线程        基本上没啥区别;
            {<!-- -->
                this.NumTow += 1;
            }
        }
        catch (Exception)
        {<!-- -->
            throw;
        }

    });
}
/// <summary>
/// new Random().Next(1, 100); 多线程同时执行结果很高概率相同,
/// 是用的当前时间为seed,时间相同结果相同
/// 
/// 解决随机数重复问题
/// 同时模拟远程请求的随机延时
/// </summary>
public class RandomHelper
{<!-- -->
    /// <summary>
    /// 随机获取数字并等待1~2s
    /// </summary>
    /// <returns></returns>
    public int GetRandomNumberDelay(int min, int max)
    {<!-- -->
        Thread.Sleep(this.GetRandomNumber(500, 1000));//随机休息一下
        return this.GetRandomNumber(min, max);
    }


    /// <summary>
    /// 获取随机数,解决重复问题
    /// </summary>
    /// <param name="min"></param>
    /// <param name="max"></param>
    /// <returns></returns>
    public int GetRandomNumber(int min, int max)
    {<!-- -->
        Guid guid = Guid.NewGuid();//每次都是全新的ID
        string sGuid = guid.ToString();
        int seed = DateTime.Now.Millisecond;
        for (int i = 0; i < sGuid.Length; i++)
        {<!-- -->
            switch (sGuid[i])
            {<!-- -->
                case 'a':
                case 'b':
                case 'c':
                case 'd':
                case 'e':
                case 'f':
                case 'g':
                    seed = seed + 1;
                    break;
                case 'h':
                case 'i':
                case 'j':
                case 'k':
                case 'l':
                case 'm':
                case 'n':
                    seed = seed + 2;
                    break;
                case 'o':
                case 'p':
                case 'q':
                case 'r':
                case 's':
                case 't':
                    seed = seed + 3;
                    break;
                case 'u':
                case 'v':
                case 'w':
                case 'x':
                case 'y':
                case 'z':
                    seed = seed + 3;
                    break;
                default:
                    seed = seed + 4;
                    break;
            }
        }
        Random random = new Random(seed);
        return random.Next(min, max);
    }
}

九:最终版(async/await)

public class AwaitAsyncClass
{<!-- -->
    public async static void TestShow()
    {<!-- -->
        Test();
    }

    private async static Task Test()
    {<!-- -->
        Console.WriteLine($"当前主线程id={Thread.CurrentThread.ManagedThreadId.ToString("00")}");
        {<!-- -->
            Task t = NoReturnTask();
            Console.WriteLine($"Main Thread Task ManagedThreadId={Thread.CurrentThread.ManagedThreadId}");
            t.Wait();//主线程等待Task的完成  阻塞的
            await t;//await后的代码会由线程池的线程执行  非阻塞
        }
        {<!-- -->
            Task<long> t = SumAsync();
            Console.WriteLine($"Main Thread Task ManagedThreadId={Thread.CurrentThread.ManagedThreadId}");
            long lResult = t.Result;//访问result   主线程等待Task的完成
            t.Wait();//等价于上一行
        }
        {<!-- -->
            Task<int> t = SumFactory();
            Console.WriteLine($"Main Thread Task ManagedThreadId={Thread.CurrentThread.ManagedThreadId}");
            long lResult = t.Result;//没有await和async 普通的task
            t.Wait();
        }
        Console.WriteLine($"Main Thread Task ManagedThreadId={Thread.CurrentThread.ManagedThreadId}");
        Console.Read();
    }

    /// <summary>
    /// 无返回值  async Task == async void
    /// Task和Task<T>能够使用await, Task.WhenAny, Task.WhenAll等方式组合使用。Async Void 不行
    /// </summary>
    /// <returns></returns>
    private static async Task NoReturnTask()
    {<!-- -->
        //这里还是主线程的id
        Console.WriteLine($"NoReturnTask Sleep before await,ThreadId={Thread.CurrentThread.ManagedThreadId}");

        Task task = Task.Run(() =>
        {<!-- -->
            Console.WriteLine($"NoReturnTask Sleep3000 before,ThreadId={Thread.CurrentThread.ManagedThreadId}");
            Thread.Sleep(3000);
            Console.WriteLine($"NoReturnTask Sleep3000 after,ThreadId={Thread.CurrentThread.ManagedThreadId}");
        });
        await task;
        Console.WriteLine($"NoReturnTask Sleep after await,ThreadId={Thread.CurrentThread.ManagedThreadId}");
    }

    /// <summary>
    /// 带返回值的Task  
    /// 要使用返回值就一定要等子线程计算完毕
    /// </summary>
    /// <returns>async 就只返回long</returns>
    private static async Task<long> SumAsync()
    {<!-- -->
        Console.WriteLine($"SumAsync 111 start ManagedThreadId={Thread.CurrentThread.ManagedThreadId}");
        long result = 0;

        await Task.Run(() =>
        {<!-- -->
            for (int k = 0; k < 10; k++)
            {<!-- -->
                Console.WriteLine($"SumAsync {k} await Task.Run ManagedThreadId={Thread.CurrentThread.ManagedThreadId}");
                Thread.Sleep(1000);
            }
            for (long i = 0; i < 999_999_999; i++)
            {<!-- -->
                result += i;
            }
        });

        Console.WriteLine($"SumFactory 111   end ManagedThreadId={Thread.CurrentThread.ManagedThreadId}");
        await Task.Run(() =>
        {<!-- -->
            for (int k = 0; k < 10; k++)
            {<!-- -->
                Console.WriteLine($"SumAsync {k} await Task.Run ManagedThreadId={Thread.CurrentThread.ManagedThreadId}");
                Thread.Sleep(1000);
            }
            for (long i = 0; i < 999999999; i++)
            {<!-- -->
                result += i;
            }
        });

        Console.WriteLine($"SumFactory 111   end ManagedThreadId={Thread.CurrentThread.ManagedThreadId}");

        await Task.Run(() =>
        {<!-- -->
            for (int k = 0; k < 10; k++)
            {<!-- -->
                Console.WriteLine($"SumAsync {k} await Task.Run ManagedThreadId={Thread.CurrentThread.ManagedThreadId}");
                Thread.Sleep(1000);
            }

            for (long i = 0; i < 999999999; i++)
            {<!-- -->
                result += i;
            }
        });

        Console.WriteLine($"SumFactory 111   end ManagedThreadId={Thread.CurrentThread.ManagedThreadId}");

        return result;
    }

    /// <summary>
    /// 真的返回Task  不是async  
    /// 
    /// 要使用返回值就一定要等子线程计算完毕
    /// </summary>
    /// <returns>没有async Task</returns>
    private static Task<int> SumFactory()
    {<!-- -->
        Console.WriteLine($"SumFactory 111 start ManagedThreadId={Thread.CurrentThread.ManagedThreadId}");
        TaskFactory taskFactory = new TaskFactory();
        Task<int> iResult = taskFactory.StartNew<int>(() =>
        {<!-- -->
            Thread.Sleep(3000);
            Console.WriteLine($"SumFactory 123 Task.Run ManagedThreadId={Thread.CurrentThread.ManagedThreadId}");
            return 123;
        });
        //Console.WriteLine($"This is {iResult.Result}");
        Console.WriteLine($"SumFactory 111   end ManagedThreadId={Thread.CurrentThread.ManagedThreadId}");
        return iResult;
    }
}
posted @ 2020-12-31 08:54  不要摸我的腰  阅读(525)  评论(0编辑  收藏  举报