.Net 异步编程之Task

       .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<string> task = Task.Factory.StartNew<string>(() => 
      {
          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<string> task = new Task<string>(() =>
{
     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<string> task = new Task<string>(() =>
{
     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<string> t = Task.Factory.StartNew<string>(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。下面解释各个枚举值的作用。

  • LongRunning:顾名思义就是长时间运行的任务,此选项建议任务调度器分配一个专用的线程给任务。这样做的原因是:长时间运行的任务可能会阻塞任务队列,导致那些短小的任务一直得不到执行。LongRunning也适合那些阻塞的任务。
  • PreferFairness:公平第一,此选项建议任务调度器尽量按照任务的启动时间来调度任务。但是它通常可能不这样做,因为它使用本地工作偷取队列(local work-stealing queues)优化任务调度。这个优化对那些非常小的任务很有用。
  • AttachToParent:附加到父任务,此选项用来创建子任务。

    关于父子任务,这里有一个例子:

    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的用法很灵活,今天没有提到异常处理,任务等待和暂停等,研究之后再写出来。


     

     

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值