Task

对于多线程,经常使用的是Thread。在了解Task之前,如果要使用多核的功能可能就会自己来开线程,然而这种线程模型在.net 4.0之后被一种称为基于“任务的编程模型”所冲击,这就是Task,Task会比Thread具有更小的性能开销,Task是架构在Thread之上的就是说Task最终还是会抛给线程去做,并且任务跟线程不是一对一的关系,比如开10个任务并不是说会开10个线程,这一点任务有点类似线程池,但是任务相比线程池有很小的开销和精确的控制。Task 类的表示单个操作不返回一个值,通常以异步方式执行,Task 对象是"基于任务的异步模式"首次引入.NET Framework 4 中。 因为由执行工作 Task 对象通常以异步方式执行在线程池线程上而不是以同步方式在主应用程序线程,可以使用Status属性,以及 IsCanceled, IsCompleted, 和 IsFaulted 属性,以确定任务的状态

创建任务的方式

使用实例化的ThreadFactory类

var t1 = new TaskFactory().StartNew(() => Console.WriteLine("TaskFactory().StartNew"));

使用Task的静态属性Task静态属性Factory

var t2 = Task.Factory.StartNew(() => Console.WriteLine("Task静态属性Factory"));

使用Task的构造函数(实例化Task对象时,任务不会立即执行,而是指定Created状态,通过Task.Start()方法启动)

var t3 = new Task(() => Console.WriteLine("使用Task的构造函数"));
t3.Start();

.Net4.5新增功能使用Task类的Run方法

var t4 = Task.Run(() => Console.WriteLine("Task.Run"));

注意:使用Task.Run/Task.Factory.StartNew/new TaskFactory().StartNew()方法运行的任务会立即开始工作,无需显式地调用这些任务的Start方法

同步任务

任务不一定要使用线程池中的线程,也可以使用其他线程,任务也可以同步进行,以相同的线程作为主调线程

static void Main(string[] args)
{
var t1 = new Task(() => TaskMethod("t1"));
t1.Start();
Console.WriteLine("主线程调用结束");
Console.ReadKey();
} public static void TaskMethod(string taskName)
{
Console.WriteLine("Task {0} 运行在线程id为{1}的线程上。是否是线程池中线程?:{2}",
taskName, Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread);
}

var t1 = new Task(() => TaskMethod("t1"));
t1.RunSynchronously();
Console.WriteLine("主线程调用结束");
Console.ReadKey();

使用单独线程的任务

如果任务的代码应该长时间运行,就应该使用TaskCreationOptions.LongRuning告诉任务调度器创建一个新线程,而不是使用线程池中的线程,此时,线程可以不受线程池管理

 var t1 = new Task(TaskMethod, TaskCreationOptions.LongRunning);
t1.Start();

Task生命周期

Created 该任务已初始化,但尚未被计划
WaitingForActivation 该任务正在等待 .NET Framework 基础结构在内部将其激活并进行计划
WaitingToRun 该任务已被计划执行,但尚未开始执行
Running 该任务正在运行,但尚未完成
WaitingForChildrenToComplete 该任务已完成执行,正在隐式等待附加的子任务完成
RanToCompletion 已成功完成执行的任务
Canceled

该任务已通过对其自身的 CancellationToken 引发 OperationCanceledException 对取消进行了确认,此时该标记处于已发送信号状态;或者在该任务开始执行之前,已向该任务的
CancellationToken 发出了信号

Faulted 由于未处理异常的原因而完成的任务

Task任务控制

Task.Wait task1.Wait();就是等待任务执行完成,task的状态变为Completed
Task.WaitAll 待所有的任务都执行完成
Task.WaitAny 等待任何一个任务完成就继续向下执行
Task.ContinueWith 第一个Task完成后自动启动下一个Task,实现Task的延续CancellationTokenSource
CancellationTokenSource 通过cancellation的tokens来取消一个Task

获取任务返回值Task<TResult>

static void Main(string[] args)
{
Task<string> t1 = new Task<string>(() => TaskMethod("t1"));
t1.Start();
Console.WriteLine(t1.Result);
Console.ReadKey();
} public static string TaskMethod(string taskName)
{
var result = string.Format("Task {0} 运行在线程id为{1}的线程上。是否是线程池中线程?:{2}",
taskName, Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread);
return result;
}

连续任务

在指定任务完成后调用另一个指定任务

static void Main(string[] args)
{
Task<string> t1 = new Task<string>(() => TaskMethod1("t1"));
Console.WriteLine("创建Task,状态为:{0}", t1.Status);
t1.Start();
Console.WriteLine("启动Task,状态为:{0}", t1.Status);
Console.WriteLine(t1.Result);
Console.WriteLine("创建Task,状态为:{0}", t1.Status);
Task t2 = t1.ContinueWith(TaskMethod2);
Console.ReadKey();
} public static string TaskMethod1(string taskName)
{
var result = string.Format("TaskID {0} 运行在线程id为{1}的线程上。是否是线程池中线程?:{2}",
taskName, Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread);
return result;
} public static void TaskMethod2(Task t)
{
Console.WriteLine("Task {0} 运行在线程id为{1}的线程上。是否是线程池中线程?:{2}",
t.Id, Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread);
}
}

使用TaskContinuationOptions枚举的值可以指定连续任务只有在起始任务成功或失败结束时启动

Task t2 = t1.ContinueWith(TaskMethod2, TaskContinuationOptions.NotOnCanceled); 

嵌套Task

在Task内部创建Task,如果父任务在子任务之前结束,父任务状态就显示为WaitingForChilderenToComplete,所有的子任务也结束时,父任务的状态就改为RanToCompletion,如果使用TaskContinuationOptions枚举值创建子任务时会有不同结果,取消父任务也会取消子任务

Task的取消

static void Main(string[] args)
{
CancellationTokenSource cts = new CancellationTokenSource();
cts.CancelAfter();
Task t1 = Task.Run(() =>
{
for (int i = ; i < ; i++)
{
if (cts.Token.IsCancellationRequested)
{
cts.Token.ThrowIfCancellationRequested();
}
else
{
Thread.Sleep();
Console.WriteLine("任务t1,共执行30次,当前第{0}次", i);
}
}
}, cts.Token);
try
{
t1.Wait();
}
catch (AggregateException e)
{
foreach (var item in e.InnerExceptions)
{
Console.WriteLine(item);
}
}
Console.ReadKey();
}

工作原理

  程序运行主线程创建->创建CancellationTokenSource对象->设置task在指定毫秒数后取消(这里是8000)->创建task并传入CancellationTokenSource对象生成的token,循环打印1~30,取消标记为true则抛出异常中止任务,false则正常输出,在输出前等待500毫秒(避免8000毫秒还没到任务就已经执行完成)->使用try/catch包裹t1.Wait()等待任务执行完成语句,并捕获处理异常.

这里任务在执行完15次的时候被取消.

 多个Task

static void Main(string[] args)
{
CancellationTokenSource cts = new CancellationTokenSource();
cts.CancelAfter();
Task[] tasks = new Task[];
tasks[] = Task.Run(() =>
{
for (int i = ; i < ; i++)
{
if (cts.Token.IsCancellationRequested)
{
cts.Token.ThrowIfCancellationRequested();
}
else
{
Thread.Sleep();
Console.WriteLine("任务t1,共执行30次,当前第{0}次", i);
}
}
}, cts.Token);
tasks[] = Task.Run(() =>
{
for (int i = ; i < ; i++)
{
if (cts.Token.IsCancellationRequested)
{
cts.Token.ThrowIfCancellationRequested();
}
else
{
Thread.Sleep();
Console.WriteLine("任务t2,共执行50次,当前第{0}次", i);
}
}
}, cts.Token);
try
{
Task.WaitAll(tasks);
}
catch (AggregateException e)
{
foreach (var item in e.InnerExceptions)
{
Console.WriteLine(item);
}
}
Console.ReadKey();
}

使用Task.WaitAll()方法传递一个Task数组,并对其中多个Task的异常进行捕获处理

CancellationTokenSource.Token.Register()

static void Main(string[] args)
{
CancellationTokenSource cts = new CancellationTokenSource();
var token = cts.Token;
cts.CancelAfter();
token.Register(Callback);
Task t1 = Task.Run(() =>
{
for (int i = ; i < ; i++)
{
if (token.IsCancellationRequested)
{
token.ThrowIfCancellationRequested();
}
else
{
Thread.Sleep();
Console.WriteLine("任务t1,共执行30次,当前第{0}次", i);
}
}
}, token);
try
{
t1.Wait();
}
catch (AggregateException e)
{
foreach (var item in e.InnerExceptions)
{
Console.WriteLine(item);
}
}
Console.ReadKey();
} static void Callback()
{
Console.WriteLine("Register登记的任务取消回调函数");
}

可使用 Register,向取消标记登记一个回调方法。应用程序调用 CancellationTokenSource 对象的 Cancel 方法时,这个回调就会运行。但是不能保证这个方法在什么时候执行,可 能在任务执行完自己的取消处理之前或之后,也可能在那个过程之中。

并行开发-Task的更多相关文章

  1. 8天玩转并行开发——第二天 Task的使用

    原文 8天玩转并行开发——第二天 Task的使用 在我们了解Task之前,如果我们要使用多核的功能可能就会自己来开线程,然而这种线程模型在.net 4.0之后被一种称为基于 “任务的编程模型”所冲击, ...

  2. C#并行开发_Thread/ThreadPool, Task/TaskFactory, Parallel

    大家好,本次讨论的是C#中的并行开发,给力吧,随着并行的概念深入,哥也赶上这个潮流了,其实之前讨论C#的异步调用或者C#中BeginInvoke或者Invoke都已经涉及了部分本篇的内容. 参考书目: ...

  3. 并行开发 2.task

    原文:8天玩转并行开发——第二天 Task的使用 在我们了解Task之前,如果我们要使用多核的功能可能就会自己来开线程,然而这种线程模型在.net 4.0之后被一种称为基于 “任务的编程模型”所冲击, ...

  4. C# 并行开发总结

    本文内容 均参考自 <C#并行高级编程> TPL 支持 数据并行(有大量数据要处理,必须对每个数据执行同样的操作, 任务并行(有好多可以并发运行的操作),流水线(任务并行和数据并行的结合体 ...

  5. 8天玩转并行开发——第八天 用VS性能向导解剖你的程序

    原文 8天玩转并行开发——第八天 用VS性能向导解剖你的程序 最后一篇,我们来说说vs的“性能向导",通常我们调试程序的性能一般会使用Stopwatch,如果希望更加系统的了解程序,我们就需 ...

  6. 并行开发-Paraller

    并行开发的概念 并行开发要做的事情就是将任务分摊给硬件线程去并行执行来达到负载和加速,传统的代码都是串行的,就一个主线程,当我们为了实现加速而开了很多工作线程,这些工作线程就是软件线程 Paralle ...

  7. 转载 三、并行编程 - Task同步机制。TreadLocal类、Lock、Interlocked、Synchronization、ConcurrentQueue以及Barrier等

    随笔 - 353, 文章 - 1, 评论 - 5, 引用 - 0 三.并行编程 - Task同步机制.TreadLocal类.Lock.Interlocked.Synchronization.Conc ...

  8. 三、并行编程 - Task同步机制。TreadLocal类、Lock、Interlocked、Synchronization、ConcurrentQueue以及Barrier等

    在并行计算中,不可避免的会碰到多个任务共享变量,实例,集合.虽然task自带了两个方法:task.ContinueWith()和Task.Factory.ContinueWhenAll()来实现任务串 ...

  9. NET中并行开发优化

    NET中并行开发优化 让我们考虑一个简单的编程挑战:对大数组中的所有元素求和.现在可以通过使用并行性来轻松优化这一点,特别是对于具有数千或数百万个元素的巨大阵列,还有理由认为,并行处理时间应该与常规时 ...

随机推荐

  1. <转>SQL Server CROSS APPLY and OUTER APPLY

    Problem SQL Server 2005 introduced the APPLY operator, which is like a join clause and it allows joi ...

  2. 像Excel一样使用python进行数据分析

    Excel是数据分析中最常用的工具,本篇文章通过python与excel的功能对比介绍如何使用python通过函数式编程完成excel中的数据处理及分析工作.在Python中pandas库用于数据处理 ...

  3. MySQL 支持的数据类型

    1.数值类型 MySQL 支持所有标准 SQL 中的数值类型,其中包括严格数值类型(integer.smallint.decimal.numeric),以及近似数值数据类型(float.real.do ...

  4. TensorFlow+Keras 02 深度学习的原理

    1 神经传递的原理 人类的神经元传递及其作用: 这里有几个关键概念: 树突 - 接受信息 轴突 - 输出信息 突触 - 传递信息 将其延伸到神经元中,示意图如下: 将上图整理成数学公式,则有 y = ...

  5. 【转载】linux系统时间自动同步:ntp

    NTP基本介绍 NTP(Network TimeProtocol,网络时间协议),使用来使本地机器与服务端机器时间保持同步的一种协议.如果我们只有一台机器那么只需要安装NTP客户端ntpdate这个包 ...

  6. SQL格式化插件—SQL Pretty Printer

    在SQL Server中我们经常需要编写各种SQL脚本,例如存储过程和函数等,由于在编写过程中,经常会进行调整,格式很乱,可读性很差.对于有强迫症的人来说,看这样的代码非常痛苦,必须要手动对代码进行格 ...

  7. ios 适配iOS11&iPhoneX的一些坑

    前阵子项目开发忙成狗,就一直没做iOS11的适配,直到XcodeGM版发布后,我胸有成竹的在iPhoneX上跑起项目,整个人都凉透了...下面总结一下我遇到的坑,不是很全面,日后补充. 导航栏 导航栏 ...

  8. [Git] 将本地分支与远程分支关联

    . . . . . 在本地工程中添加Git,并将其与远程的空库关联起来,只需如下几步. 1. 创建空库 $ git init Initialized empty Git repository in D ...

  9. /usr/bin/uwsgi --http :8888 --wsgi-file wsgi.py --master --processes 4 --threads 2

    /usr/bin/uwsgi --http :8888 --wsgi-file wsgi.py --master --processes 4 --threads 2 root 18756 0.0 0. ...

  10. 交换上的FLAPPING事件 (zhuan)

    今天在学校的一个三层交换上看到持续的日志信息: Host 00:E0:FC:09:BC:F9is flapping between fa0/x and fa0/y. 思科对此官方的解释是: Error ...