C#及Java异步编程介绍

Posted 全栈人生

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C#及Java异步编程介绍相关的知识,希望对你有一定的参考价值。

1.异步编程的好处

程序(线程)在访问网络或其它IO设备时,往往需要等待相关操作完成后才能继续下一步操作,这种模式称为阻塞式的编程。C#在3.5及之前的版本基本都是提供阻塞式编程的API。

异步编程是一种非阻塞式编程,异步编程不一定能缩短计算时间,但是却能提升并发服务能力。

举一个生活中的例子,如果把计算机提供服务看作是餐厅对顾客提供服务:

服务员(CPU内核,多个服务员相当于多个内核);顾客通过服务员点了菜单(CPU分配了计算任务线程),服务员跑到厨房把菜单给后厨(网络操作或其它耗时IO操作),假如烹饪一道菜需要10分钟,如果是阻塞式编程模式,该服务员将一直守在厨房等待10分钟等到菜煮好了然后端给顾客。在阻塞式编程模式下,为N个顾客提供服务就需要N个服务员。

如果是异步编程模式,服务员在把菜单交给后厨后继续为其他顾客提供服务(线程切换),等到后厨通知菜煮好了(Task Complete),服务员根据菜单(记录了线程切换所需的上下文内容)继续为之前的顾客提供上菜服务。在异步编程模式下,一个服务员能为多个顾客提供服务,提升了服务能力。

Web系统提供访问服务,当操作需要的耗时很长,如果是阻塞式的编程,服务器将被迫创建更多的线程来接受新的访问请求,操作系统能创建的线程是有限的,并且线程切换也产生额外的性能损耗,这时带来的是能够支撑的并发用户数的急速下降。


2. C#早期版本的异步编程方式

C#早期版本使用BeginXXX、EndXXX方法和IAsyncResult接口实现异步,另外还有一个基于事件的编程模型,它也提供了XxxAsync方法(不返回Task对象),能在异步操作完成时调用事件处理程序。不过这两种编程模式都已经过时,在C#4.5版本后一般使用Task实现异步操作。

 

3.Task介绍

C#从4.0版本开始提供了Task类来实现异步操作。Task实现了IAsyncResult接口。IAsyncResult接口的成员属性IsCompleted、AsyncState可以用来判断操作是否已经完成,以及异步状态。

IAsyncResult的另一个成员属性WaitHandle AsyncWaitHandle提供了WaitAny、WaitAll等等待操作。

创建Task的几种方式:


(1) 通过Tas.Run创建:

Task t = Task.Run( () => {  /*dosomething*/  } );

给Run方法传入的参数实际上是一个委托(Action或Func)

(2) 通过TaskFactory.StartNew方法创建:

Task t = Task.Factory.StartNew( () => {  /*dosomething*/  } );

(3)通过new 创建:

Task t = new Task(()=> { /*dosomething*/ });

t.Start();


与Thread类不同,Task的泛型版本直接支持返回值,例如:

Task<int> t = Task.Run(() => {

           /*do something*/

          return 1;

  });


Thread类的Start能给线程执行传入一个Object类型的参数,而Task的执行函数内部可以直接访问上下文:

string para1 = "hello ";

            string para2 = "tencent";

            Task<string> t = Task.Run(() =>

            {

                /*do something*/

                return para1 + para2;

            });


4.Task的运行

Task创建后,由调度器TaskScheduler负责调度运行。TaskFactory.StartNew有重载版本可以指定采用的是哪个TaskScheduler,默认情况下采用的是TaskScheduler.Default调度器(ThreadPoolTaskScheduler),由线程池中的线程执行Task任务。在C#中,Task比Thread轻量级,创建Task所需的资源也比Thread少。

ThreadPoolTaskScheduler运行Task的示意代码如下:

protected internal override void QueueTask(Task task)

        {

            if ((task.Options & TaskCreationOptions.LongRunning) != 0)

            {

                // 创建Task的Options包含LongRunning参数,一般用在长时间后台运行的任务.

                Thread thread = new Thread(s_longRunningThreadWork);

                thread.IsBackground = true; // Keep this thread from blocking process shutdown

                thread.Start(task);

            }

            else

            {

                // 一般普通Task任务的处理.

               bool forceToGlobalQueue = ((task.Options & TaskCreationOptions.PreferFairness) != 0);

               ThreadPool.UnsafeQueueCustomWorkItem(task, forceToGlobalQueue);

            }

        }


5. C#中的 Await和Async关键字

Await和Async关键字可以说是C#的语法糖,用await关键字调用异步方法,编译器会自动生成基于状态机的内部临时类,该临时类注册异步任务完成的回调函数后线程立即返回,任务完成后状态机内部state改变,继续执行await语句的下一行代码。

 

6.Java的异步任务

在Java中与C#的Task比较接近的类是FutureTask。FutureTask类实现了接口Future。接口Future表示在未来异步计算能完成的结果。

Future接口主要成员:

FutureTask类实现了Future接口,同时也实现了Runnable接口,所以能被ExecutorService调用。例如:

FutureTask<String> future =

        new FutureTask<String>(new Callable<String>() {

            public String call() {

                return "Hello world!";

            }});

ExecutorService executor= Executors.newCachedThreadPool();

executor.execute(future);

System.out.println(future.get(2, TimeUnit.SECONDS));

 

ExecutorService接口提供了执行、停止、取消等管理异步任务的方法。异步方法最终在线程里面执行,根据不用的应用场景,Executors类提供了多个静态方法创建不同的线程池,举例如下:

如果需要更详细的控制,也可以直接创建ThreadPoolExecutor对象,指定更详细的参数。

 

7.结束语

C#和java对异步编程都已经提供了支持。C#的async和await关键字降低了异步编程门槛。此外.net和java框架都提供了一系列并发容器、并发工具类及并发原子操作类,在异步编程时往往也需要同时用到这些类。

以上是关于C#及Java异步编程介绍的主要内容,如果未能解决你的问题,请参考以下文章

[C#] 异步编程 - 剖析异步方法

C#与C++的发展历程第三 - C#5.0异步编程巅峰

C#并发编程之异步编程(线程讨论)

C#中异步编程多个异常的处理方式

c#异步编程小例子

step3-异步编程模式(C#篇)