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异步编程介绍的主要内容,如果未能解决你的问题,请参考以下文章