异步(Asynchronous) 使用异步创建快速响应和可伸缩性的应用程序

异步(Asynchronous) 使用异步创建快速响应和可伸缩性的应用程序

本节导读:

本节主要说明使用异步进行程序设计的优缺点及如何通过异步编程.

使用传统方法BeginInvoke / EndInvoke来实现异步。

使用async/await 创建异步方法和事件。

通过异步编程创建具有快速响应和可伸缩性的应用程序。

读前必备:

A.委托 [.net 面向对象编程基础] (21) 委托

B.事件 [.net 面向对象编程基础] (22) 事件

1.异步程序设计的优缺点:

A.让用户界面快速响应;对于耗时操作阻塞UI线程,通过异步回调可使用UI快速响应。

B.创建高伸缩性的应用。对于服务端应用,创建更多线程来处理消耗资源较多,使用异步可使用主线程继续工作,不需要等待返回。使用程序具有更好的伸缩性。

对于异步在代码调试中跟并发一样,执行时需要对cpu资源进行抢夺,从而跳来跳去

线程同步就是多个线程一个一个来,直到一个线程执行完 ,才能开始下一个线程;线程异步就是两个线程可以同时执行,不需要一个线程执行完另一个线程才可以执行

对于异步的缺点,最主要一点就是比创建同步程序难度大一些,首先使用传统的方法创建异步,比起同步更容易出错。不过随着.NET的不断发展和第三方异步组件的丰富,创建异步应用程序也变得越来越简单了。

2.异步的实现

对于.NET中的异步编程,.NET在各个方向都几乎提供了同步和异步的两个方式来实现,在这里我们不能把.NET中全部的异步编程方法都列举出来了,下面介绍几种常用且实用的异步方法。

3.使用BeginInvoke / EndInvoke实现异步

3.1 简单的异步示例

using System;
using System.Threading;

namespace Test2
{
    class Program
    {
        static void Main(string[] args)
        {
            //系统定义的委托
            Func<string> func = new Func<string>(()=>
            {
                Thread.Sleep(100);
                return "我是异步执行完成的返回值:" + DateTime.Now.ToString();
            });
            IAsyncResult asyncResult = func.BeginInvoke(null, null);    //异步调用开始
            while (!asyncResult.IsCompleted) //异步未执行完时主线程仍可以运行
            {
                Console.WriteLine("异步调用还未完成,主线程执行中...");
            }
            string result = func.EndInvoke(asyncResult);    //异步调用结束返回异步结果
            Console.WriteLine(result);
            Console.ReadKey();
        }
    }
}

运行结果如下:

在这里插入图片描述

在异步没有完成时,可以继续工作做一些想做的事,异步完成后返回结果。

3.2 使用异步的超时 WaitOne 判断异步完成

除了上面使用IsCompleted来判断异步完成之外,也可以使用WaitOne 来判断异步的完成情况

示例如下 :

//使用一个有返回值的泛型委托来执行BeginInvoke
Func<string> myFunc = new Func<string>(()=>{
    int i = 0;
    while (i<99999999)
        ++i;
    return "异步执行完成的返回值" + (i).ToString() + " 当前时间:" + System.DateTime.Now.ToString();
   
});
IAsyncResult asynResult = myFunc.BeginInvoke(null, null);

while (!asynResult.AsyncWaitHandle.WaitOne(10, false))//阻塞当前主线程,运行异步线程
    Console.Write("*");

string result = myFunc.EndInvoke(asynResult);
Console.Write("\n");
Console.WriteLine(result);

运行结果如下:

在这里插入图片描述

下面的代码是为了理解mre.WaitOne(100000, false)中退出与不退出同步域的区别

using System;
using System.Diagnostics;
using System.Runtime.Remoting.Contexts;
using System.Threading;
using System.Threading.Tasks;

namespace MultithreadingApplication
{
    class Program
    {
        static void Main(string[] args)
        {
            BaseClass firstClass = new BaseClass();
            ChildClass secondClass = firstClass.GetChildClass();
            Thread firstThread = new Thread(firstClass.method);
            Thread secondThread = new Thread(secondClass.method);
            firstThread.Start();
            Thread.Sleep(3000);//先让firstThread执行起来
            secondThread.Start();
            Console.Read();
        }
    }

    [Synchronization(true)]//同步域需要这个特性标注并需要继承ContextBoundObject类吗?
    class BaseClass : ContextBoundObject//BaseClass继承了ContextBoundObject,并且Synchronization修饰,是一个同步域
    {
        public static ManualResetEvent mre = new ManualResetEvent(false);
        public static Stopwatch stopwatch = new Stopwatch();
        public virtual void method()
        {
            stopwatch.Start();
            Console.WriteLine("BaseClass method begin");
            mre.WaitOne(100000, false);//如果为true,先退出同步域,此时ChildClass可以执行了;如果为false,先不退出,只有BaseClass执行完,ChildClass才能执行
            Console.WriteLine("BaseClass method end after {0} ms", stopwatch.ElapsedMilliseconds);
            stopwatch.Stop();
            //个人见解:false 先不退出同步域,即此时开启的三个线程,只有主线程可以继续执行,因为firstThread线程被阻塞且不退出同步域,所以secondThread也不能执行(secondThread与firstThread同属一个同步域)
            //同步:就是一件事一件事来做,就像java中的方法调用,只有当前执行的方法执行完了,下面的方法才能开始调用执行.
        }

        public ChildClass GetChildClass()
        {
            return new ChildClass();
        }      
    }
    class ChildClass : BaseClass//继承了BaseClass,ChildClass和BaseClass属于同一个同步域
    {
        public override void method()
        {
            Console.WriteLine("ChildClass1 method entered synchromization domain after {0} ms", stopwatch.ElapsedMilliseconds);
        }
    } 
}

3.3 回调

毕竟上述两种等待不是一个好的方法。我们在前端开发中使用过ajax的同学肯定知道, 前端中异步使用一个回调函数,在异步完成后完成我们想要做的事,.NET自然也有类似的回调方法,看示例:

                        //使用一个有返回值的泛型委托来执行BeginInvoke
            Func<string> myFunc = new Func<string>(() => {
                int i = 0;
                while (i < 9)
                    ++i;
                return "异步执行完成的返回值" + (i).ToString() + " 当前时间:" + System.DateTime.Now.ToString();                 
            });
            IAsyncResult asynResult = myFunc.BeginInvoke((result) =>//BeginInvoke(AsyncCallBack callback, object @object)中
            {    //第一个参数是一个回调lambda表达式,表达式里的result参数是asynResult 第二个参数不知道是干嘛的.
                string rst = myFunc.EndInvoke(result);
                Console.WriteLine("异步完成了,我该返回结果了!");//当异步线程(即myFun)走完后,会进入该语句块执行里面的表达式(即异步完成后完成我们想要做的事)
                Console.WriteLine(rst);
            }, null);
            Console.ReadKey();

运行结果如下 :

在这里插入图片描述

下面代码是自己写的带参数返回值的异步委托,回调时的情况

using System;

namespace MultithreadingApplication
{
    delegate string MyDelegate(string s);
    class Program
    {
        public string M(string s)
        {
            return s;
        }
        static void Main(string[] args)
        {
            Program program = new Program();
            MyDelegate myDelegate = new MyDelegate(program.M);
            IAsyncResult asyncResult = myDelegate.BeginInvoke("sssss",(ia) =>
             {//第一个参数就是委托参数;第二个参数是lambda表达式,其中ia是asyncResult;最后一个参数不知道是干嘛的
                 string rs = myDelegate.EndInvoke(ia);
                 Console.WriteLine("异步执行完成,返回结果");
                 Console.WriteLine(rs);
             },null);
            Console.ReadKey();
        }
    }
}

3.4 其它组件中的Begin\End异步方法

除了BeginInvoke / EndInvoke之外,.NET在很多类中提供了异步的方法,

如System.Net.HttpWebRequest类的BeginGetResponse和EndGetResponse方法,

这里不再一一列举了,使用方法和上面的示例类似。

4. async/await

.NET 5.0 以后,让异步编程变得更加简单了,我们介绍一下async和await。

它让我们编写异步程序变得和同步一样简单,不但减少了代码量,而且不会因为异步让我们程序逻辑被打乱。

4.1 异步方法

下面使用async 和 await关键字来创建一个异步方法,

在第一个方法里调用第二个异步方法,

第二个异步方法中使用了多线程。

听起来很绕口,不过整个代码编写和同步方法没有什么区别,只是多一个关键字。

using System;
using System.Threading;
using System.Threading.Tasks;

namespace MultithreadingApplication
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("主线程开始..");
            AsyncMethod();
            Thread.Sleep(1000);
            Console.WriteLine("主线程结束..");
            Console.ReadKey();

        }
        static async void AsyncMethod()
        {
            Console.WriteLine("开始异步方法");
            var result = await MyMethod();//await 运算符暂停对其所属的 async 方法的求值,直到其操作数表示的异步操作完成
            Console.WriteLine("异步方法结束");
        }
        static async Task<int> MyMethod()//Task<int>表示一个可返回值的异步操作,<int>为返回的数据类型
        {
            for (int i = 0; i < 5; i++)
            {
                Console.WriteLine("异步执行" + i.ToString() + "...");
                await Task.Delay(1000); //模拟耗时操作
            }
            return 0;
        }
    }
}

运行结果如下:

在这里插入图片描述

5. 本节要点

A.使用传统方法BeginInvoke / EndInvoke来实现异步

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值