C# 异步编程 学习 十三: Task介绍及其示例

Task 的异常

  1. 与Thread不一样,Task可以很方便的传播异常,
  2. 如果你的task 里边抛出一个未处理的异常(故障) 那么该异常就会重新被抛出,调用了 Wait() 方法
  3. 调用了Task 的Result属性方法的地方。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
using System.Data.SqlClient;

namespace ConsoleApp3
{
class Program
{
static void Main(string[] args)
{
Task task = Task.Run(()=> {
throw null;
});

        try
        {
            task.Wait();
        }
        catch (AggregateException aex)
        {
            if (aex.InnerExceptions is NullReferenceException)
            {
                Console.WriteLine("Null");
            }
            else
            {
                throw;
            }
        }
       
        Console.ReadKey(true);
    }
}

}

CLR 将异常包裹在 AggregateException 里,以便在并行编程场景中发挥很好地作用。

Task 的异常

  1. 如果无需重新抛出异常,通过Task 的 IsFaulted 和 IsCanceled 属性可以检测Task 是否发生了故障。
  2. 若果两个属性都返回false 那么就没有错误发生。
  3. 如果IsCanceled 为true, 那就说明OperationCanceledException 为该Task 抛出了。
  4. 如果IsFaulted 为true, 那么就说明另一个异常被抛出,而Exception 属性也将指明错误。

异常与 “自治” 的Task

  1. 自治的 设置完就不管了的Task 就是指不通过调用Wait() 方法,Result属性或者 continuation 进行会和任务。
  2. 针对自治Task 需要向Thread一样, 显示的处理异常, 避免发生 悄无声息的故障。
  3. 自治的Task上 未处理的异常称为微观察到异常。

未观察到异常

  1. 可以通过全局 TaskScheduler.UnobservedTaskException 来订阅未观察到异常。
  2. 关于是 未观察的异常, 有一些细节差别。
  3. 在Task 发生故障后 ,如果访问Task的 Exception 属性 ,那么就该异常就被认为是已观察到的。

Continuation 继续,延续

  1. 当结束线程时候,在做点其他事情。
  2. Continuation 通过回调的方法实现的,当操作一结束就进行执行。
  3. 在primeNuBerTask 上调用GetAwaiter() 就可以得到一个 awaiter 对象。
  4. 他的 OnCompeleted方法就会告诉之前的task “当你结束或者发生故障,要执行的委托”
  5. 可以将Continuation 附加到已经结束的task上 此时Continuation 将会安排立即执行。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
using System.Data.SqlClient;

namespace ConsoleApp3
{
class Program
{
static void Main(string[] args)
{
TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException;
Task primeNuBerTask = Task.Run(()=>
Enumerable.Range(1, 2000000).Count(ret=>
Enumerable.Range(2,(int)Math.Sqrt(ret)-1).All(i=>ret%i >0)));

        var awaiter = primeNuBerTask.GetAwaiter();
        awaiter.OnCompleted(()=> {
            int result = awaiter.GetResult();
            Console.WriteLine(result);
        });

        Console.ReadKey(true);
    }

    private static void TaskScheduler_UnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e)
    {
        Console.WriteLine("全局异常处理事件");
    }
}

}

awaiter 等待者

  1. 任何可以暴露下列两个方法和一个属性的对象 就是awaiter :
    OnCompleted 和 GetResult
  2. 一个叫做IsCompleted 的 bool 属性
  3. 没有接口或者父类来统一这些成员,其中 OnCompleted 是 INotifyCompletion 的一部分。

若果发生故障

  1. 如果之前的任务发生故障, 那么当Continuation 代码调用awaiter。GetResult() 时候,异常就会重新抛出。
  2. 无需调用GetResult 我们可以直接访问task 的Result属性。
  3. 但调用GetResult 的好处就是,如果Task发生故障,那么异常就被被直接抛出,而不是包裹在AggreateException 里边, 这样的话catch 块就简洁很多。

非泛型Task

  1. 针对非泛型task GetResult() 方法有一个void 返回值, 他就是用来重新抛出异常。

同步上下文

  1. 如果同步上下文出现了,那么OnCompelted会自动捕获他, 并将Continuation 提交到这个上下文中。这一点在富客户端非常有用,因为他会把Continuation 放回到UI线程中。
  2. 如果是编写一个库,则不希望出现上述行为,应为开销比较大的UI线程切换应该是在程序运行离开库时候只发生一次,而不是出现方法回调,所以,我们可以使用ConfigureAwait 方法来避免这种行为。
  3. 如果没有同步上下文出现,或者你使用的是ConfigureAwait(false) 那么continuation 会运行在先前task的同一个线程上,从而避免不必要的开销。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
using System.Data.SqlClient;

namespace ConsoleApp3
{
class Program
{
static void Main(string[] args)
{
TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException;
Task primeNuBerTask = Task.Run(()=>
Enumerable.Range(1, 2000000).Count(ret=>
Enumerable.Range(2,(int)Math.Sqrt(ret)-1).All(i=>ret%i >0)));

        var awaiter = primeNuBerTask.ConfigureAwait(false).GetAwaiter();
        awaiter.OnCompleted(()=> {
            int result = awaiter.GetResult();
            Console.WriteLine(result);
        });

        Console.ReadKey(true);
    }

    private static void TaskScheduler_UnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e)
    {
        Console.WriteLine("全局异常处理事件");
    }
}

}

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
using System.Data.SqlClient;
using System.Xml;

///For Text Two
namespace ConsoleApp3
{
class Program
{
static void Main(string[] args)
{
TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException;
Task primeNuBerTask = Task.Run(()=>
Enumerable.Range(1, 2000000).Count(ret=>
Enumerable.Range(2,(int)Math.Sqrt(ret)-1).All(i=>ret%i >0)));

        primeNuBerTask.ContinueWith(retTask =>
        {
            int retsult = retTask.Result;
            Console.WriteLine(retsult);
        });

        Console.ReadKey(true);
    }

    private static void TaskScheduler_UnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e)
    {
        Console.WriteLine("全局异常处理事件");
    }
}

}

ContinueWitch

  1. 另外一种附加Continuation 的方式就是调用task的 ContinueWith 方法
  2. ContinueWith 本身就返回一个task 他可以用他来附加更多的Continuation ,但是必须直接处理AggregateException
  3. 如果task发生故障,需要写额外的代码来吧Continuation封装到UI上。
  4. 在非UI上下中,如果想让Continuation 和task 执行在同一个线程上,就必须指定taskContinuationOptions。ExecuteSynchronouly, 否则他将弹回到线程池。

TaskCompletionSource

  1. Task.Run 创建Task。 另外一种方式就是用TaskCompletionSource 来创建Task
  2. TaskCompletionSource 让你稍后开始和结束的任意操作中创建Task。
  3. 他会为你提供一个可手动执行的 从属Task ,指示操作何时发生何时结束。
  4. 他对IO-Bound 类工作比较理想。可以获取所有task的好处。(传播值 , 异常 ,Continuation等)。 不需要在操作时阻塞线程。

使用 TaskCompletionSource

  1. 初始化一个实例即可。他有一个Task 属性可以 返回一个Task。
  2. 该 Task 完全由TaskCompletionSource 对象控制。
  3. 调用任意一个方法就会给Task发信号: 完成,故障,取消。
  4. 这些方法只能调用一次,如果再次调用就会抛出异常。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
using System.Data.SqlClient;
using System.Xml;

namespace ConsoleApp3
{
class Program
{
static void Main(string[] args)
{
TaskCompletionSource tcs = new TaskCompletionSource();

        new Thread(() =>
        {
            Thread.Sleep(5000);
            tcs.SetResult(42);
        })
        { IsBackground = true }.Start();

        Task<int> task = tcs.Task;
        Console.WriteLine(task.Result);
        Console.ReadKey(true);
    }
}

}

for text two

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
using System.Data.SqlClient;
using System.Xml;

namespace ConsoleApp3
{
class Program
{
static void Main(string[] args)
{
Task task = Run(() => {
Thread.Sleep(5000);
return 10;
});
Console.WriteLine(task.Result);
Console.ReadKey(true);
}

  static  Task<TResult> Run<TResult>(Func<TResult> func)
    {
        TaskCompletionSource<TResult> task = new TaskCompletionSource<TResult>();
        new Thread(() => {
            try
            {
                task.SetResult(func());
            }
            catch (Exception ex )
            {

                task.SetException(ex);
            }
        }).Start();

        return task.Task;
    }
  
}

internal class TResult
{
}

}

TaskCompletionSource的真正作用

  1. 他创建Task 但并不占用线程。

for text One Timer

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
using System.Data.SqlClient;
using System.Xml;

namespace ConsoleApp3
{
class Program
{
static void Main(string[] args)
{
var awaiter = GetAnswerToLife().GetAwaiter();
awaiter.OnCompleted(() => {
Console.WriteLine(awaiter.GetResult());
});
Console.ReadKey(true);
}

    static Task<int> GetAnswerToLife()
    {
        var tcs = new TaskCompletionSource<int>();
        var timer = new System.Timers.Timer(5000) { AutoReset = false };
        timer.Elapsed += delegate { timer.Dispose();tcs.SetResult(40); };
        timer.Start();
        return tcs.Task;

    }

    
}

}

for text Delay

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApp4
{
class Program
{
static void Main(string[] args)
{
Delay(1000).ContinueWith(retTask => {
Console.WriteLine(retTask.IsFaulted);

        });

        Console.ReadKey();
    }

    static Task Delay(int millisecond)
    {
        TaskCompletionSource<object> tsc = new TaskCompletionSource<object>();
        var timer = new System.Timers.Timer(3000) {  AutoReset=false};
        timer.Elapsed += delegate { timer.Dispose();tsc.SetResult(null); };
        timer.Start();
        return tsc.Task;
    }
}

}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

望天hous

你的鼓励是我最大动力~谢谢啦!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值