c#编程细节(五)多线程与异步

概述

/// 进程 线程 多线程 计算机概念
/// 进程:一个程序运行时,占用的全部计算资源的总和
/// 线程:程序执行流的最小单位;任何操作都是由线程完成的;
///       线程是依托于进程存在的,一个进程可以包含多个线程;
///       线程也可以有自己的计算资源
/// 多线程:多个执行流同时运行
///        1 CPU太快了,分时间片--上下文切换(加载环境--计算--保存环境)
///          微观角度,一个核同一时刻只能执行一个线程;宏观的来说是多线程并发
///        2 多CPU多核  可以独立工作
///          4核8线程--核是物理的核 线程是指虚拟核
///          
///   Thread是C#语言对线程对象的封装 
///   
/// 是对方法执行的描述
/// 同步:完成计算之后,再进入下一行
/// 异步:不会等待方法的完成,会直接进入下一行  非阻塞
/// 
/// C# 异步和多线程有什么差别
/// 多线程就是多个thread并发;
/// 异步是硬件式的异步
/// 异步多线程--thread pool task
   /// 异步方法
    /// 1 同步方法卡界面,主(UI)线程忙于计算;
    ///   异步多线程方法不卡界面,主线程完事儿了,计算任务交给子线程在做;
    ///   winform提升用户体验;web一个业务操作后要发邮件,异步发送邮件
    /// 
    /// 2 同步方法慢,只有一个线程干活;
    ///   异步多线程方法快,因为多个线程并发运算;
    ///   并不是线性增长,a资源换时间,可能资源不够   b 多线程也有管理成本
    ///   但并不是越多越好
    ///   多个独立任务可以同时运行;
    ///   
    /// 3 异步多线程无序:启动无序  执行时间不确定  结束也无序
    ///   一定不要通过等几毫秒的形式来控制启动/执行时间/结束
    ///   
    /// 回调/状态等待/信号量

回调

callback回调函数会在action执行完执行,第三个参数"hao"可以通过Console.WriteLine(ia.AsyncState);显示出来。

Action<string> action = this.DoSomethingLong;

            //IAsyncResult asyncResult = null;

            //AsyncCallback callback = ia =>
            //{
            //    //Console.WriteLine(object.ReferenceEquals(asyncResult, ia));
            //    //Console.WriteLine(ia.AsyncState);
            //    //Console.WriteLine($"到这里计算已经完成了。{Thread.CurrentThread.ManagedThreadId.ToString("00")}。");
            //};
            //asyncResult = action.BeginInvoke("btnAsyncAdvanced_Click", callback, "hao");

c#的多线程发展中主要有3次大变动,分别thread,threadpool,task
目前task是主流,前两种就不记录了
Task简介

什么时候用多线程? 任务能并发运行;提升速度;优化体验
例:
////一个业务查询操作有多个数据源 首页–多线程并发–拿到全部数据后才能返回 WaitAll
////一个商品搜素操作有多个数据源,商品搜索–多个数据源–多线程并发–只需要一个结果即可–WaitAny

任务的并行:parallel

多线程中的异常处理

在try里面最后加一个Task.WaitAll(taskList.ToArray());才能捕捉到异常,类型为AggregateException,
否则线程里面的异常是会被吞掉了,因为已经脱离try catch的范围了

线程取消

多个线程并发,某个失败后,希望通知别的线程,都停下来
task是外部无法中止,Thread.Abort不靠谱,因为线程是OS的资源,无法掌控啥时候取消
线程自己停止自己–公共的访问变量–修改它—线程不断的检测它(延迟少不了)

               ////CancellationTokenSource去标志任务是否取消  Cancel取消   IsCancellationRequested  是否已经取消了
                ////Token 启动Task的时候传入,那么如果Cancel了,这个任务会放弃启动,抛出一个异常
                //CancellationTokenSource cts = new CancellationTokenSource();//bool值 //bool flag = true;
                //for (int i = 0; i < 40; i++)
                //{
                //    string name = string.Format("btnThreadCore_Click{0}", i);
                //    Action<object> act = t =>
                //    {
                //        try
                //        {
                //            //if (cts.IsCancellationRequested)
                //            //{
                //            //    Console.WriteLine("{0} 取消一个任务的执行", t);
                //            //}
                //            Thread.Sleep(2000);
                //            if (t.ToString().Equals("btnThreadCore_Click11"))
                //            {
                //                throw new Exception(string.Format("{0} 执行失败", t));
                //            }
                //            if (t.ToString().Equals("btnThreadCore_Click12"))
                //            {
                //                throw new Exception(string.Format("{0} 执行失败", t));
                //            }
                //            if (cts.IsCancellationRequested)//检查信号量
                //            {
                //                Console.WriteLine("{0} 放弃执行", t);
                //                return;
                //            }
                //            else
                //            {
                //                Console.WriteLine("{0} 执行成功", t);
                //            }
                //        }
                //        catch (Exception ex)
                //        {
                //            cts.Cancel();
                //            Console.WriteLine(ex.Message);
                //        }
                //    };
                //    taskList.Add(taskFactory.StartNew(act, name, cts.Token));
                //}
                //Task.WaitAll(taskList.ToArray());

多线程临时变量

          //i最后是5      全程就只有一个i  等着打印的时候,i==5
            //k             全程有5个k   分别是0 1 2 3 4 
            //k在外面声明   全程就只有一个k    等着打印的时候,k==4
            //int k = 0;
            //for (int i = 0; i < 5; i++)
            //{
            //    k = i;
            //    new Action(() =>
            //    {
            //        Thread.Sleep(100);
            //        Console.WriteLine($"k={k} i={i}");
            //    }).BeginInvoke(null, null);
            //}

异步执行并且线程停止了100,最后运行读取i时有已经自增为5

线程安全

//lock 解决,因为只有一个线程可以进去,没有并发,所以解决了问题 但是牺牲了性能,所以要尽量缩小lock的范围
//不要冲突–数据拆分,避免冲突
//安全队列 ConcurrentQueue 一个线程去完成操作

https://blog.csdn.net/jiangxinyu/article/details/5380969

发布了44 篇原创文章 · 获赞 8 · 访问量 4131

猜你喜欢

转载自blog.csdn.net/MaYang_/article/details/104071978