.NET里面的Task能够很方便地写出多线程的操作方法,比使用Thead类更加优雅。.NET 有一个组件-BackGroundWorker,也是支持异步编程的重要方面,在UI界面操作,需要等待较长时间的时候,我们就可以使用这些异步的方法去执行这些耗时的操作,UI线程的操作可以继续,后台线程执行完之后,给UI返回一个结果,这时候我们就很优雅地解决了多个线程之间的交互问题。
Android平台也通常会遇到这种问题,使用的是Android的looper机制,通过消息队列和消息处理器来进行处理,由此可以,异步编程无论是.NET还是Android都在原始的异步编程方式上进行了封装,虽然每种语言实现的方式不一样,但是基本思想都是一致的。最近希望从.NET平台的异步编程Task入手,回顾并总结一下各种异步编程的方式,会将Android的looper机制。
言归正传,我们来看看Task.我们先来看看例子。
static void Main(string[] args)
{
Console.WriteLine("start.");
Task.Factory.StartNew(() =>
{
for (int i = 0; i < 5; i++)
{
Thread.Sleep(100);
Console.WriteLine("Async : " + i);
}
});
Console.WriteLine("end.");
Console.ReadKey();
}
这个例子里面,Task通过TaskFactory创建了一个Task(工厂模式),Task调用StartNew,这句话貌似没有说完,开始一个新的什么呢?不过仔细想想,不需要说完,为什么?因为肯定是一个"动作",这个动作可以是方法,当然也可以是委托,Lamda表达式也是极好的(*_*)。方法和委托其实代表的就是一种动作,说明白了反而有歧义!我们查看StartNew参数列表之后发现,确实是需要一个Action的委托传入。经过这么分析,那结果应该就是:
刚才我们注意到,我们的Task是没有返回值的,其实通常我们通过后台线程去做一件事情,希望做完了反馈一个结果,比如下载文件,返回一个下载成功的Message等等。下面我们就来看看有返回值的Task是怎么使用的。
static void Main(string[] args)
{
Console.WriteLine("start.");
Task task = Task.Factory.StartNew(() =>
{
for (int i = 0; i < 5; i++)
{
Thread.Sleep(100);
Console.WriteLine("Async : " + i);
}
return "async success!";
});
Console.WriteLine("end.");
Console.WriteLine("Task result is :" + task.Result);
Console.ReadKey();
}
代码逻辑就不再分析了,异步执行完会返回一个结果。执行结果如下:
刚才是自动启动这个过程,其实启动过程是可以手动控制的,代码如下:
Console.WriteLine("start.");
Task task = new Task(() =>
{
for (int i = 0; i < 5; i++)
{
Thread.Sleep(100);
Console.WriteLine("Async : " + i);
}
return "async success!";
});
task.Start();
Console.WriteLine("end.");
Console.WriteLine("Task result is :" + task.Result);
我们通常看到Task就想当然地认为是异步运行的,其实我们可以灵活控制其运行方式,让它有时候异步运行,有时候同步运行。代码如下:
Console.WriteLine("start.");
Task task = new Task(() =>
{
for (int i = 0; i < 5; i++)
{
Thread.Sleep(100);
Console.WriteLine("Async : " + i);
}
return "async success!";
});
task.RunSynchronously();
Console.WriteLine("end.");
Console.WriteLine("Task result is :" + task.Result);
与上面代码的不同在于task.RunSynchronously();意思是这个task与当前线程同步运行!结果:
其实,Task还可以指定Task运行状态值,并且可以指定创建的Task的属性。
Console.WriteLine("start.");
Task t = Task.Factory.StartNew(state =>
{
for (int i = 0; i < 8; i++)
{
Thread.Sleep(1000);
Console.WriteLine("hello" + state);
}
return "Async Result.";
},"MyAsync");
Console.WriteLine("end.");
Console.WriteLine(t.AsyncState);
Console.WriteLine(t.Result);
最后补充一点,可以指定一个任务创建选项(TaskCreationOptions) ,这个枚举类型有以下枚举值:None,LongRunning,PreferFairness,AttachedToParent。下面解释各个枚举值的作用。
关于父子任务,这里有一个例子:
Console.WriteLine("start.");
var parent = new Task(() =>
{
var nonChildTask = Task.Factory.StartNew(
() => Console.WriteLine("I'm not a child task.")
);
var childTask = Task.Factory.StartNew(
() => Console.WriteLine("I'm a child task."),
TaskCreationOptions.AttachedToParent);
});
parent.Start();
Console.WriteLine("end.");
运行结果:
总结:Task的用法很灵活,今天没有提到异常处理,任务等待和暂停等,研究之后再写出来。