六、委托的同步和异步

1.进程与线程

进程:计算机概念,程序在运行的时候,记录当前程序对计算机的各种资源的消耗的一种记录,虚拟出来的概念
线程:计算机概念,线程是计算机在执行某个动作时,一个最小的执行流,虚拟出来的概念
包含关系:一个进程包含多个线程

进程和线程的超详细理解
C#中的多线程:Thread是对计算机资源操作的一种封装类
为什么可以有多个线程?
1.多个CPU可以有多个核进行并行计算,计算机具有超强的计算能力,一个线程去操作某一个计算
2.CPU分片,比如1s能处理1000000次计算,可以吧1s内的处理能力再进一步切分,操作系统去调用执行不同的计算
从宏观来讲:就相当于有很多个任务可以去并发执行
从微观来讲:一个物理CPU同一时刻,只能为一个任务服务

2.同步方法和异步方法

同步方法:发起调用,代码按照顺序一行一行地执行,线程ID是同一个,即同一个线程来执行所有的操作
就像是请人吃饭:诚心诚意地请人吃饭,邀请“牧舟”吃饭,可是“牧舟”现在有点忙,那就等他忙完一起去吃
异步方法:发起调用,没有等待完成,直接进入到下一行,发现线程ID不一样,即启动一个新的线程来执行动作
也像是请人吃饭:只是客气一下请人吃饭,邀请“牧舟”吃饭,“牧舟”现在有点忙,那你先忙,我先去吃
异步方法和同步方法的区别
1.异步方法不卡顿界面,UI线程即主线程闲置,计算任务都交给子线程去执行了
同步方法卡顿界面,因为主线程忙于计算,无暇他顾
异步方法改善了用户体验,场景:发送短信、邮件等
2.同步方法慢,异步方法快,因为多线程并发,以资源换性能

/// <summary>
/// 同步方法
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnSyncClick(object sender, EventArgs e)
{
    //Thread.CurrentThread.ManagedThreadId对标于每个线程的一个ID
    Console.WriteLine($"--- btnSyncClick Start 线程ID{Thread.CurrentThread.ManagedThreadId.ToString("00")}  {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}");
    for(int i = 0; i < 5; i++)
    {
        string name = string.Format($"btnSyncClick-{i}");
        this.DoSomethingLong(name);
    }
    Console.WriteLine($"--- btnSyncClick End 线程ID{Thread.CurrentThread.ManagedThreadId.ToString("00")}  {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}");
    Console.WriteLine();
}
/// <summary>
/// 异步方法
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnAsyncClick(object sender, EventArgs e)
{
    Console.WriteLine($"-- -- btnAsyncClick Start {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}");
    Action<string> action = this.DoSomethingLong;
    //action.Invoke("btnAsyncClick");

    //action.BeginInvoke("btnAsyncClick", null, null);//会分配一个新的线程去执行,.NetCore还不支持

    for(int i = 0; i < 5; i++)
    {
        string name = string.Format($"btnAsyncClick-{i}");
        action.BeginInvoke(name, null, null);
    }

    Console.WriteLine($"-- -- btnAsyncClick End {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}");
    Console.WriteLine();
}
/// <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;
    for(int i = 0; i < 1000_000_000; i++)
    {
        lResult += i;
    }
    //Thread.Sleep(200);//不消耗资源,只是等待
    Console.WriteLine($"-- -- DoSomethingLong End {name} {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}  {lResult}");
}

同步方法和异步方法耗时对比
3.同步方法按顺序执行,异步方法无序性,由于线程资源是向操作系统申请的,由操作系统的调度策略来决定,所以启动顺序是随机的,执行和结束也是无顺序的

3.委托的异步回调

private void btnAsyncAdvancedClick(object sender, EventArgs e)
{
    Console.WriteLine($"-- - btnAsyncAdvancedClick Start {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}");
    Action<string> action = this.DoSomethingLong;

    //通过异步回调委托
    //BeginInvoke第一个参数是异步委托所需要的参数
    //如这里的参数btnAsyncAdvancedClick就是DoSomethingLong里的name
    //BeginInvoke第二个参数是异步委托操作的结果
    //BeginInvoke第三个参数是异步委托回调中所需要的参数
    //action.BeginInvoke("btnAsyncAdvancedClick", null, null);
    //for(int i = 0; i < 3; i++)
    //{
    //    string name = string.Format($"btnAsyncAdvancedClick==={i}");
    //    action.BeginInvoke(name, null, null);
    //}
    Console.WriteLine();
    //如果想要返回值呢?
    Func<string, int> func = s =>
     {
         Console.WriteLine($"func委托需要的参数:{s}");
         return DateTime.Now.Day;
     };
    //表示异步操作的状态
    IAsyncResult asyncResultfunc = null;
    IAsyncResult asyncResultaction = null;
    //asyncResult = func.BeginInvoke("btnAsyncAdvancedClick", null, null);
    //int day = func.EndInvoke(asyncResult);
    //Console.WriteLine($"Func委托返回值:{day}");
    Console.WriteLine();

    //现在有个需求,需要在DoSomethingLong所有计算完成以后,输出一句话“btnAsyncAdvancedClick计算成功了……”
    //如果按照同步方法的思路,直接在上述代码下面加一句输出会发现,并不一定是DoSomethingLong所有计算完成以后才会输出
    //更多的情况是那句话输出后子线程依然在执行DoSomethingLong方法
    Console.WriteLine("同步方法思路:  btnAsyncAdvancedClick计算成功了……");
    //要实现上述操作,该怎么做?
    //就相当于一堆动作执行完毕以后,再执行一点动作
    AsyncCallback callbackfunc = ar =>
    {
        int dayResult = func.EndInvoke(ar);//主线程会等待,func的结果要到这里来拿,必须要执行完毕
        //在异步委托的内部,可以通过AsyncState获取到参数
        Console.WriteLine($"异步操作完成后调用  btnAsyncAdvancedClick计算成功了……  回调中需要的参数是:{ar.AsyncState} 今天的日期是:{dayResult}");
    };//这叫异步回调,是一个委托
    asyncResultfunc = func.BeginInvoke("btnAsyncAdvancedClick", callbackfunc, "牧舟");
    
    AsyncCallback callbackaction = ara =>
    {
        Console.WriteLine($"action异步操作完成后调用 btnAsyncAdvancedClick计算成功了…… 回调中需要的参数是:{ara.AsyncState}");
    };
    asyncResultaction = action.BeginInvoke("btnAsyncAdvancedClick", callbackaction, "牧舟");

    int j = 0;
    while (!asyncResultaction.IsCompleted)//表示前面的动作是否已经完成了
    {
        if (j < 9)
        {
            Console.WriteLine($"高级班多线程的第一节课已完成{++j * 10}%......");
        }
        else
        {
            Console.WriteLine($"高级班多线程的第一节课已完成99.9999%...");
        }
        Thread.Sleep(200);
        //++j;
    }

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

委托的异步调用
IAsynResult表示异步操作的状态

IAsynResult asynResult = func.BeginInvoke("btnAsyncAdvancedClick", callbackfunc, "牧舟");
asynResult.AsynWaitHandle.WaitOne();//可以做到等待异步委托执行完毕
asynResult.AsynWaitHandle.WaitOne(-1);//一直等待任务完成
asynResult.AsynWaitHandle.WaitOne(2000);//限时等待,比如这个最多等待2s,过时不候,会阻塞主线程
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值