• C#线程:任务Task


    Task是一个更高级的抽象概念,它代表了一个并发操作,而该操作并不一定依赖线程来完成。Task是可以组合的(可以将它们通过延续(continuation)操作串联在一起)。它们可以使用线程池减少启动延迟,也可以通过TaskCompletionSource采用回调的方式避免多个线程同时等待I/O密集型操作。

    Task类是Framework 4.0时作为并行编程库的组成部分引入的。然而它们后来经历了许多改进(通过使用等待器(awaiter)),从而在常见的并发场景中发挥了越来越大的作用。Task类也是C#异步功能的基础类型。

    1.启动任务

    启动一个基于线程的Task的最简单方式是使用Task.Run(Task类位于System.Threading.Tasks命名空间)静态方法。调用时只需传入一个Action委托:

    Task.Run(()=> Console.WriteLine("foo"));
    

    和下面的方式很相似:

    new Thread (()=> Console.WriteLine("foo")).Start();
    

    Task.Run会返回一个Task对象,它可以用于监控任务的执行过程。这一点与Thread对象不同。我们可以使用Task的Status属性来追踪其执行状态。

    Wait方法

    调用Task的Wait方法可以阻塞当前方法,直到任务完成,这和调用线程对象的Join方法类似:

    Task task = Task.Run (() =>
    {
        Console.WriteLine ("Task started");
        Thread.Sleep (2000);
        Console.WriteLine ("Foo");
    });
    Console.WriteLine (task.IsCompleted);  // False
    task.Wait();
    

    可以在Wait中指定一个超时时间和取消令牌(可选)来提前终止等待状态。

    长任务

    默认情况下,CLR会将任务运行在线程池线程上,这种线程非常适合执行短小的计算密集的任务。如果要执行长时间阻塞的操作(如上面的例子),则可以按照以下方式避免使用线程池线程:

    Task task = Task.Factory.StartNew(()=>......,TaskCreationOptions.LongRunning);
    

    2.返回值

    Task有一个泛型子类Task<TResult>,它允许任务返回一个值。如果在调用Task.Run时传入一个Func<TResult>委托(或者兼容的Lambda表达式)替代Action就可以获得一个Task<TResult>对象,通过查询Result属性就可以获得任务的返回值。如果当前任务还没有执行完毕,调用该属性会阻塞当前线程,直至任务结束。

    Task<int> task = Task.Run(() =>
    {
        Console.WriteLine("Foo");
        return 3;
    });
    int result = task.Result; 
    

    可以将Task理解为一个“未来值”,它封装了Result并将在以后生效。

    3.异常

    任务可以方便地传播异常,这和线程是截然不同的。因此,如果任务中的代码抛出一个未处理异常,那么调用Wait()或者访问Task的Result属性时,该异常就会被重新抛出。

    Task task = Task.Run (() => { throw null; });
    try 
    {
        task.Wait();
    }
    catch (AggregateException aex)
    {
        if (aex.InnerException is NullReferenceException)
            Console.WriteLine ("Null!");
        else
            throw;
    }
    

    使用Task的IsFaulted和IsCanceled属性可以在不抛出异常的情况下检测出错的任务。如果IsCanceled为true,则说明任务抛出了OperationCanceledException;如果IsFaulted为true,则说明任务抛出了其他类型的异常,通过Exception属性可以了解该异常的信息。

    4.延续

    延续会告知任务在完成后继续执行后续的操作。延续通常由回调方法实现,该方法会在操作完成后执行。
    以下是计算素数的例子:

    Task<int> primeNumberTask = Task.Run (() =>
        Enumerable.Range (2, 3000000).Count (n => 
            Enumerable.Range (2, (int)Math.Sqrt(n)-1).All (i => n % i > 0)));
    
    var awaiter = primeNumberTask.GetAwaiter();
    awaiter.OnCompleted (() => 
    {
        int result = awaiter.GetResult();
        Console.WriteLine (result);      
    });
    

    调用任务的GetAwaiter方法将返回一个awaiter对象。这个对象的OnCompleted方法告知先导任务(primeNumberTask)当它执行完毕(或者出现错误)时调用一个委托。将延续附加到一个已执行完毕的任务上是完全没有问题的,此时,延续的逻辑将会立即执行。

    如果先导任务出现错误,则延续代码调用awaiter.GetResult()时会重新抛出异常。对于非泛型任务,GetResult的返回值为void,这个函数的用途完全是为了重新抛出异常。

    5.TaskCompletionSource类

    另一种创建任务的方法是使用TaskCompletionSource,这种任务并非那种需要执行启动操作并在随后停止的任务;而是在操作结束或出错时手动创建的“附属”任务。这非常适用于I/O密集型的工作:它不但可以利用任务所有的优点(能够传递返回值、异常或延续),而且不需要在操作执行期间阻塞线程。

    TaskCompletionSource的用法很简单,直接进行实例化即可。它包含一个Task属性,返回一个Task对象。
    下面的例子会在等待5秒钟后输出42:

    var tcs = new TaskCompletionSource<int>();
    new Thread (() => 
    { 
        Thread.Sleep (5000); 
        tcs.SetResult (42); 
    }).Start();
    
    Task<int> task = tcs.Task;      
    Console.WriteLine (task.Result);
    

    6.Task.Delay方法

    它是Task类的一个静态方法,是Thread.Sleep的异步版本,不会造成阻塞。

  • 相关阅读:
    php程序员面试经验
    模板引擎(smarty)知识点总结五
    模板引擎(smarty)知识点总结三
    js计算字数
    phpexcel导出成绩表
    phpexcel用法(转)
    模板引擎(smarty)知识点总结
    apache泛域名解析
    对象的解构赋值
    解构赋值
  • 原文地址:https://www.cnblogs.com/nullcodeworld/p/16689242.html
Copyright © 2020-2023  润新知