C#多线程编程的Task(任务全面解析)

原文链接:https://www.cnblogs.com/xietianjiao/p/7429742.html

Task是.NET4.0加入的,跟线程池ThreadPool的功能类似,用Task开启新任务时,会从线程池中调用线程,而Thread每次实例化都会创建一个新的线程。

我们可以说Task是一种基于任务的编程模型。它与thread的主要区别是,它更加方便对线程进程调度和获取线程的执行结果。

Task类和Task类前者接收的是Action委托类型后者接收的是Func委托类型

任务和线程的区别:

1、任务是架构在线程之上的,也就是说任务最终还是要抛给线程去执行。

2、任务跟线程不是一对一的关系,比如开10个任务并不是说会开10个线程,这一点任务有点类似线程池,但是任务相比线程池有很小的开销和精确的控制。

一、Task的创建

1、直接创建

var task1 = new Task(() =>

{

Console.WriteLine("Begin");

System.Threading.Thread.Sleep(5000);

Console.WriteLine("Finish");

});

Console.WriteLine("Before start:" + task1.Status);

task1.Start();

2、工厂创建

Task.Factory.StartNew(()={

});

3、4.5以后Run运行

Task.Run(()=>{

});

4、一种方便获取返回值的方式

static void Main(string[] args)

{

var tcs = new TaskCompletionSource();

new Thread(() => {

Thread.Sleep(5000);

int i = Enumerable.Range(1, 100).Sum();

tcs.SetResult(i); }).Start();//线程把运行计算结果,设为tcs的Result。

Task task = tcs.Task;

Console.WriteLine(task.Result); //此处会阻塞,直到匿名线程调用tcs.SetResult(i)完毕

}

二、细节解释

看下面代码:

namespace WpfApplication6

{

///

/// MainWindow.xaml 的交互逻辑

///

public partial class MainWindow : Window

{

public MainWindow()

{

InitializeComponent();

ConsoleManager.Show();//打开控制台窗口

}

private void Window_Loaded(object sender, RoutedEventArgs e)

{

Console.WriteLine("主线程启动");

Task task = Task.Run(() => {

Thread.Sleep(1500);

Console.WriteLine("task启动");

});

Thread.Sleep(300);

task.Wait();

Console.WriteLine("主线程结束");

}

}

}

结果:

分析:

开启新任务的方法:Task.Run()或者Task.Factory.StartNew(),开启的是后台线程

要在主线程中等待后台线程执行完毕,可以使用Wait方法(会以同步的方式来执行)。不用Wait则会以异步的方式来执行。

thread和Task的区别,thread new多少个就会创建多少个线程,而task是利用线程池中的线程。

task就是有返回值的Task,TResult就是返回值类型。示例:

private void Window_Loaded(object sender, RoutedEventArgs e)

{

Console.WriteLine("主线程开始");

//返回值类型为string

Task task = Task.Run(() => {

Thread.Sleep(2000);

return Thread.CurrentThread.ManagedThreadId.ToString();

});

//会等到task执行完毕才会输出;

Console.WriteLine(task.Result);

Console.WriteLine("主线程结束");

}

通过task.Result可以取到返回值,若取值的时候,后台线程还没执行完,则会等待其执行完毕!

简单提一下:

Task任务可以通过CancellationTokenSource类来取消。

三、Task的其他方法

Task.Wait(); //阻塞当前线程

Task.WaitAll(); //阻塞当前线程直到所有的任务执行完毕

Task.WaitAny(); //阻塞当前线程直到有任意一个任务执行完毕

Task.ContinueWith(

task=>{

}); //执行完上一个任务后继续执行,并将上一个任务(包括结果)传递给下一个代码块

一种是使用GetAwaiter方法。GetAwaiter方法返回一个TaskAwaiter结构,该结构有一个OnCompleted事件,只需对

OnCompleted事件赋值,即可在完成后调用该事件。

static void Main(string[] args)

{

Task Task1 = Task.Run(() => { return Enumerable.Range(1, 100).Sum(); });

var awaiter = Task1.GetAwaiter();

awaiter.OnCompleted(() =>

{

Console.WriteLine("Task1 finished");

int result = awaiter.GetResult();

Console.WriteLine(result); // Writes result

});

Thread.Sleep(1000);

}

四、任务的中断

var tokenSource = new CancellationTokenSource();

var token = tokenSource.Token;

var task = Task.Factory.StartNew(() =>

{

for (var i = 0; i < 1000; i++)

{

System.Threading.Thread.Sleep(1000);

if (token.IsCancellationRequested)

{

Console.WriteLine("Abort mission success!");

return;

}

}

}, token);

//注册cancel后要执行的代码

token.Register(() =>

{

Console.WriteLine("Canceled");

});

Console.WriteLine("Press enter to cancel task...");

Console.ReadKey();

//调用取消

tokenSource.Cancel();

五、任务的中断取消

var tokenSource = new CancellationTokenSource();

var token = tokenSource.Token;

var task = Task.Factory.StartNew(() =>

{

for (var i = 0; i < 1000; i++)

{

System.Threading.Thread.Sleep(1000);

if (token.IsCancellationRequested)

{

Console.WriteLine("Abort mission success!");

return;

}

}

}, token);

//注册cancel后要执行的代码

token.Register(() =>

{

Console.WriteLine("Canceled");

});

Console.WriteLine("Press enter to cancel task...");

Console.ReadKey();

//调用取消

tokenSource.Cancel();

六、异常处理

异常处理;

对于某些匿名的Task(通过 Task.Run方法生成的,不调用wait,也不关心是否运行完成),某些情况下,记录它们的异

常错误也是有必要的。这些异常称作未观察到的异常(unobserved exceptions)。可以通过订阅一个全局的静态事件

TaskScheduler.UnobservedTaskException来处理这些异常。只要当一个Task有异常,并且在被垃圾回收的时候,才会触

发这一个事件。如果Task还处于被引用状态,或者只要GC不回收这个Task,这个UnobservedTaskException事件就不会被

触发

GC.Collect();

GC.WaitForPendingFinalizers();

七、Task的状态

Created:表示默认初始化任务,但是“工厂创建的”实例直接跳过。

WaitingToRun: 这种状态表示等待任务调度器分配线程给任务执行。

RanToCompletion:任务执行完毕。