csharp 异步编程
当有多个任务需要同时执行,或者有比较费事的操作时但不想阻塞主线程的时候,我们往往会使用到多线程编程,但是多线程编程本身需要很强的
多线程编程思维,需要解决诸多问题:如线程锁、线程间的数据同步等问题。csharp提供了异步编程的模式,.net提供了三种模式:基于任务的
异步编程(TAP)、基于事件的异步编程(EAP)、异步编程模式(APM)三种模式
TAP
csharp虽然提供了三种不同的异步编程的模式,但是相对于EAP和APM,TAP使用起来更简单易用,TAP是基于task的异步编程模式,使用一个方
法就能完成所有的操作。从代码阅读角度去看,TAP的整理逻辑和同步的逻辑基本一样。具体的EAP和APM使用方式可以参见下面的介绍,它们都涉
及到多个方法,才能完成异步操作。当异步操作比较多的时候,EAP和APM都会会陷入callback hell。
我们在设计游戏中的新手引导的时候,经常会有一些强制引导。比如下面的流程:
以前设计的时候,我们可能使用event设计模式,通过事件的监听与发布来解决此类问题。下面是一个伪代码,真实的设计中并不会真的这么设计,
这里只是为了说明callback hell的问题。下面的代码我们都是放在了guid类中,可能看起来还不算复杂,有的会将各事件的处理分布在不同的
函数中,这时就更头痛了。
public class Guild
{public Guild(){evnet.register("onPlayMove", onPlayMove);evnet.register("onGetBox", onGetBox);//...}public void Move(Vector3 postion){//pick box.}public void onGetBox(){//open bag.}
}
当时我们使用TAP模式编程时,上面的问题就会变的简单。
public class Guild
{public Guild(){await Player.Move(postion);await Player.PickItem();await openBag();await Player.UserItem();//....}
}
EAP
基于事件的异步模式,一般会有两个相应的API:MethodNameAsync和OnMethodNameCompleted.比如WebClient中的download相关的api
ManualResetEvent waitWeb = new ManualResetEvent(false);
WebClient webClient = new WebClient();
webClient.DownloadStringCompleted += (sender, eventArgs) =>
{Console.WriteLine("content length:{0}",eventArgs.Result.Length);Console.WriteLine("xxxx");waitWeb.Set();
};webClient.DownloadStringAsync(new Uri("http://www.baidu.com"));
Console.WriteLine("start download");
waitWeb.WaitOne();
Console.WriteLine("over");
执行结果
start download
content length:9193
xxxx
over
APM
APM是基于IAsyncResult设计模式完成的异步操作,具体是通过命名为BeginOperationName和EndOperationName的两个方法来实现的。
begin和end分别用于开始和结束并获取异步结果。例如Action中一个对BeginInvoke和EndInvoke,以及strem中的BeginRead和EndRead
这些api都是基于APM模式设计的。
public class Demo{public string Test(double seconds){Console.WriteLine("Test Start");Thread.Sleep(TimeSpan.FromSeconds(seconds));Console.WriteLine("Test End");return $"Test cost:{seconds}s";}}class Program{delegate string AsyncCall(double seconds);static void Main(string[] args){Console.WriteLine("Hello World!");Demo demo = new Demo();AsyncCall asyncCall = new AsyncCall(demo.Test);var asyncResult = asyncCall.BeginInvoke(5, null, null);\Console.WriteLine("main sleep");Thread.Sleep(1000);Console.WriteLine("main sleep end!");Console.WriteLine(asyncCall.EndInvoke(asyncResult));Console.WriteLine("goodby!");}}
执行结果
Hello World!
main sleep
Test Start
main sleep end!
Test End
Test cost:5s
goodby!