C#多线程——任务并行库TPL

2025-12-13 0 743

1 什么是TPL

T
P
L
\\textcolor{red}{TPL}
TPL
(Task Parallel Library)任务并行库,是从.NetFramwork4.0后引入的基于异步操作的一组API。TPL的底层是基于多线程实现的,但是它相较于直接使用多线程,更为简单,它向程序员隐藏了与线程池交互的底层代码。在.NetFramwork4.0后,微软更推荐程序员使用TPL去编写多线程代码或者并行代码。
TPL的核心是任务,一个任务代表了一个异步操作,该操作可以使用或不适用独立的线程运行。
一个任务可以和其他的任务组合起来,比如同时启动多个任务,等待所有的任务完成;对之前所有任务的结果进行计算,TPL的优势在于具有组合任务API,而不用单独书写线程同步的代码(关注于锁、线程间的信号)。同样在多线程中关于多线程中异常的传播与处理是极为复杂的,而在TPL中,可以通过 A
g
g
r
e
g
a
t
e
E
x
c
e
p
t
i
o
n
\\textcolor{red}{AggregateException}
AggregateException
,捕获底层任务的所有异常,并允许单独处理这些异常。

2 创建与启动任务

创建、启动任务使用到的关键类如下:类定义在 S
y
s
t
e
m
.
T
h
r
e
a
d
i
n
g
.
T
a
s
k
s
\\textcolor{red}{System.Threading.Tasks}
System.Threading.Tasks
命名空间中
C#多线程——任务并行库TPL

public static void Main(string[] args)
{
void TaskMethod(string name) {
Console.WriteLine(\”Task{0} is runing on ThreadId {1} Is ThreadPool Thread {2}\”,
name,
Thread.CurrentThread.ManagedThreadId,
Thread.CurrentThread.IsThreadPoolThread);
}

int TaskMethod_1(string name) {

Console.WriteLine(\”Task{0} is runing on ThreadId {1} Is ThreadPool Thread {2}\”,
name,
Thread.CurrentThread.ManagedThreadId,
Thread.CurrentThread.IsThreadPoolThread);
return 0;
}

#region 1 创建任务
TaskMethod(\”Main\”);
//public Task(Action action) 传入一个无返回的委托函数
Task t1 = new Task(() => TaskMethod(\”t1\”));
Task t2 = new Task(() => TaskMethod(\”t2\”));
t1.Start();//显示创建Task需要运行start方法才能运行任务中的方法
t2.RunSynchronously(); //RunSynchronously()在当前线程上运行任务方法
Task.Run(() => TaskMethod(\”t3\”));
Task.Factory.StartNew(() => TaskMethod(\”t4\”));

//Task<T> 管理有返回值的工作单元 public Task(Func<TResult> function)
//通过传入有返回的委托函数来创建任务对象
Task<int> t5 = new Task<int>(() => TaskMethod_1(\”t5\”));
t5.Start();
Console.WriteLine(t5.Result); //获得任务结果

Console.ReadKey();
#endregion

}

C#多线程——任务并行库TPL

3 等待任务

有以下方式可以等待任务完成:

  • 调用Wait方法(可选择指定超时时间)
  • 访问Result属性(当使用Task时)
  • Task.WaitAll(等待所有指定任务完成)
  • Task.WaitAny(等待任意一个任务完成)。

#region 2 等待任务

Console.OutputEncoding = Encoding.Unicode;
Console.WriteLine(\”起始执行时间为{0}\”, DateTime.Now.ToString(\”yyyy-MM-dd HH:mm:ss\”));
List<Task> list = new List<Task>();

for (int i = 1; i <= 5; i++)
{
int tempI = i; //需要使用局部变量,因为for循环在数据量很小的时候,for循环结束时 task启动了,但是可能还未执行。由于共享变量i,所有在真正执行Task时,线程名称将一样 i=11
Task t = new Task(()=>TaskMethod_2(\”-\”+ tempI, tempI));
list.Add(t);
t.Start();//因为多线程的启动并不意味着立马进行,需要等待操作系统的调度。
}

Task.WaitAll(list.ToArray());//等待所有的线程完成
Task.WaitAny(list.ToArray());//等待任意一个线程完成后执行,相当于在一个ManualResetEventSlim上等待,

Console.WriteLine(\”等待所有任务线程执行完成,结束执行时间为{0}\”, DateTime.Now.ToString(\”yyyy-MM-dd HH:mm:ss\”));

Task<int> t_1 = new Task<int>(() => TaskMethod_2(\”t_1\”, 10));
t_1.Start();
t_1.Wait();//主线程等待t_1线程完成任务方法。 Wait(TimeSpan timeout) 也可以等待具体的时间
Console.WriteLine(t_1.Result);

Console.WriteLine(\”结束执行时间为{0}\”, DateTime.Now.ToString(\”yyyy-MM-dd HH:mm:ss\”));
Console.ReadKey();
#endregion

TaskMethod_2 方法如示例一代码块。方法运行后如下

C#多线程——任务并行库TPL

4 任务中的异常处理

#region 3 任务的异常处理
Task t1 = Task.Factory.StartNew(() => {
throw new Exception(\”Task Failed ! \”);
});
try
{
t1.Wait();//当你等待一个任务结束时(通过调用Wait方法或访问其Result属性),所有未处理的异常都会用一个AggregateException对象封装,方便重新抛给调用方。
}
catch (AggregateException aex)
{
Console.WriteLine(aex.InnerException.Message); // Task Failed !
}

//定义一个父子任务,在父任务与子任务中分别抛出异常
TaskCreationOptions atp = TaskCreationOptions.AttachedToParent;
var parent = Task.Factory.StartNew(() =>
{
Console.WriteLine(\”I am Parent\”);

Task.Factory.StartNew(() => // 子
{
Console.WriteLine(\”I am Child\”);
throw new Exception(\”Child Exception\”);
}, atp);

throw new Exception(\”Parent Exception\”);

});

try {
parent.Wait();
}
catch (AggregateException aex)
{
Console.WriteLine(aex.InnerExceptions.Count); //2 捕获了父异常和子异常
//这是对于有父子关系的任务,在父任务上等待也会隐式的等待子任务,所有子任务的
//异常也会传递出来。
}

Console.ReadKey();
#endregion

main 方法块如上,运行结果如下图所示
C#多线程——任务并行库TPL

当某个任务抛出一个或多个异常时,异常包装在 A
g
g
r
e
g
a
t
e
E
x
c
e
p
t
i
o
n
\\textcolor{blue}{AggregateException }
AggregateException
异常中。 该异常会传播回与任务联接的线程。 通常,该线程是 等待任务完成
\\textcolor{red}{等待任务完成}
等待任务完成
的线程
(1)Wait方法
(2)WaitAny方法
(3)WaitAll方法
或访问 Result 属性的线程。AggregateException 通常包含关联任务线程中的所有异常,我们可以在外部通过try catch的方式去处理它,但是这并不意味着并不需要单独处理任务线程的异常,否则可能会因为无解的异常导致程式的中断。

5 取消任务

任务的取消需要用到 C
a
n
c
e
l
l
a
t
i
o
n
T
o
k
e
n
S
o
u
r
c
e
\\textcolor{red}{CancellationTokenSource}
CancellationTokenSource
C
a
n
c
e
l
l
a
t
i
o
n
T
o
k
e
n
\\textcolor{red}{CancellationToken}
CancellationToken
类。在取消任务的过程中需要了解一下几点:

  • 可以在构造中传入CancellationToken来构建Task任务,并且CancellationToken可以绑定到多个任务上
  • Task的创建和执行都是独立的,如果在任务执行前取消了任务,那么任务代码将不会执行。如果尝试调用start方法,将会抛出异常InvalidOperationException
  • 任务执行后去执行CancellationTokenSource.Cancel方法,任务不会被取消
  • 需要在任务代码中 显示定义任务中断的逻辑
    \\textcolor{blue}{显示定义任务中断的逻辑}
    显示定义任务中断的逻辑

Main 方法代码块如下

#region 5 取消任务

Console.OutputEncoding = System.Text.Encoding.UTF8;
void RunTask(string name) {
for (int i = 0; i < 10; i++) {
Console.WriteLine(name+\”运行开始:\” + i);
Thread.Sleep(1000);
}
}

void RunTaskWithCancellationToken(string name,CancellationToken token) {
for (int i = 0; i < 10; i++)
{
Console.WriteLine(name + \”运行开始:\” + i);
Thread.Sleep(1000);
token.ThrowIfCancellationRequested(); //在运行的Task任务中,显示定义任务取消的逻辑
}
}

CancellationTokenSource source = new CancellationTokenSource();
CancellationToken cancellation = source.Token;

//创建一个和CancellationToken关联的任务类
//t1任务不支持显示取消任务
Task t1 = Task.Factory.StartNew(() => RunTask(\”t1\”), cancellation);
//t2任务支持显示取消任务,在任务执行的逻辑中加了ThrowIfCancellationRequested,标识希望在任务中断是抛出异常
Task t2 = Task.Factory.StartNew(() => RunTaskWithCancellationToken(\”t2\”,cancellation), cancellation);
//t3任务不支持显示取消任务,且需要手动start去运行任务代码。
Task t3 = new Task(()=>RunTask(\”t1\”),cancellation);

Thread.Sleep(TimeSpan.FromSeconds(3));

source.Cancel();

try
{
//在一个任务调度前,取消任务,那么将会抛出System.InvalidOperationException 标识在已经完成的工作上呼叫start动作。
t3.Start();
}
catch (Exception e)
{

Console.WriteLine(\”异常类型{0} ,异常消息{1}\”,e.GetType().Name,e.Message);
}

Console.WriteLine(\”t1 IsCanceled {0} ,t1 IsCompleted{1}, t1 IsFaulted{2}, t1 status{3}\”, t1.IsCanceled, t1.IsCompleted, t1.IsFaulted, t1.Status.ToString());
Console.WriteLine(\”t2 IsCanceled {0} ,t2 IsCompleted{1}, t2 IsFaulted{2}, t2 status{3}\”, t2.IsCanceled, t2.IsCompleted, t2.IsFaulted, t2.Status.ToString());
Console.WriteLine(\”t3 IsCanceled {0} ,t3 IsCompleted{1}, t3 IsFaulted{2}, t3 status{3}\”, t3.IsCanceled, t3.IsCompleted, t3.IsFaulted, t3.Status.ToString());
Console.ReadKey();
#endregion

运行方法后如下
C#多线程——任务并行库TPL
当主线程不休眠3s (注释掉 Thread.Sleep(TimeSpan.FromSeconds(3)))后,验证结论一
C#多线程——任务并行库TPL

收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

申明:本文由第三方发布,内容仅代表作者观点,与本网站无关。对本文以及其中全部或者部分内容的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。本网发布或转载文章出于传递更多信息之目的,并不意味着赞同其观点或证实其描述,也不代表本网对其真实性负责。

左子网 编程相关 C#多线程——任务并行库TPL https://www.zuozi.net/36574.html

常见问题
  • 1、自动:拍下后,点击(下载)链接即可下载;2、手动:拍下后,联系卖家发放即可或者联系官方找开发者发货。
查看详情
  • 1、源码默认交易周期:手动发货商品为1-3天,并且用户付款金额将会进入平台担保直到交易完成或者3-7天即可发放,如遇纠纷无限期延长收款金额直至纠纷解决或者退款!;
查看详情
  • 1、描述:源码描述(含标题)与实际源码不一致的(例:货不对板); 2、演示:有演示站时,与实际源码小于95%一致的(但描述中有”不保证完全一样、有变化的可能性”类似显著声明的除外); 3、发货:不发货可无理由退款; 4、安装:免费提供安装服务的源码但卖家不履行的; 5、收费:价格虚标,额外收取其他费用的(但描述中有显著声明或双方交易前有商定的除外); 6、其他:如质量方面的硬性常规问题BUG等。 注:经核实符合上述任一,均支持退款,但卖家予以积极解决问题则除外。
查看详情
  • 1、左子会对双方交易的过程及交易商品的快照进行永久存档,以确保交易的真实、有效、安全! 2、左子无法对如“永久包更新”、“永久技术支持”等类似交易之后的商家承诺做担保,请买家自行鉴别; 3、在源码同时有网站演示与图片演示,且站演与图演不一致时,默认按图演作为纠纷评判依据(特别声明或有商定除外); 4、在没有”无任何正当退款依据”的前提下,商品写有”一旦售出,概不支持退款”等类似的声明,视为无效声明; 5、在未拍下前,双方在QQ上所商定的交易内容,亦可成为纠纷评判依据(商定与描述冲突时,商定为准); 6、因聊天记录可作为纠纷评判依据,故双方联系时,只与对方在左子上所留的QQ、手机号沟通,以防对方不承认自我承诺。 7、虽然交易产生纠纷的几率很小,但一定要保留如聊天记录、手机短信等这样的重要信息,以防产生纠纷时便于左子介入快速处理。
查看详情

相关文章

猜你喜欢
发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务