C# 并行和多线程编程——认识和使用Task
作者:雲霏霏 发布时间:2022-03-28 05:48:04
目录
任务和线程的区别:
一、认识Task和Task的基本使用
1、认识Task
2、创建Task
二、Task的任务控制
1、Task.Wait
2、Task.WaitAll
3、Task.WaitAny
4、Task.ContinueWith
5、Task的取消
对于多线程,我们经常使用的是Thread。在我们了解Task之前,如果我们要使用多核的功能可能就会自己来开线程,然而这种线程模型在.net 4.0之后被一种称为基于“任务的编程模型”所冲击,因为task会比thread具有更小的性能开销,不过大家肯定会有疑惑,任务和线程到底有什么区别呢?
任务和线程的区别:
1、任务是架构在线程之上的,也就是说任务最终还是要抛给线程去执行。
2、任务跟线程不是一对一的关系,比如开10个任务并不是说会开10个线程,这一点任务有点类似线程池,但是任务相比线程池有很小的开销和精确的控制。
一、认识Task和Task的基本使用
1、认识Task
首先来看一下Task的继承结构。Task标识一个异步操作。
可以看到Task和Thread一样,位于System.Threading命名空间下,这也就是说他们直接有密不可分的联系。下面我们来仔细看一下吧!
2、创建Task
创建Task的方法有两种,一种是直接创建——new一个出来,一种是通过工厂创建。下面来看一下这两种创建方法:
//第一种创建方式,直接实例化
var task1 = new Task(() =>
{
//TODO you code
});
这是最简单的创建方法,可以看到其构造函数是一个Action,其构造函数有如下几种,比较常用的是前两种。
//第二种创建方式,工厂创建
var task2 = Task.Factory.StartNew(() =>
{
//TODO you code
});
这种方式通过静态工厂,创建以个Task并运行。下面我们来建一个控制台项目,演示一下,代码如下:
要添加System.Threading.Tasks命名控件引用。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TaskDemo
{
class Program
{
static void Main(string[] args)
{
var task1 = new Task(() =>
{
Console.WriteLine("Hello,task");
});
task1.Start();
var task2 = Task.Factory.StartNew(() =>
{
Console.WriteLine("Hello,task started by task factory");
});
Console.Read();
}
}
}
这里我分别用两种方式创建两个task,并让他们运行。可以看到通过构造函数创建的task,必须手动Start,而通过工厂创建的Task直接就启动了。
下面我们来看一下Task的声明周期,编写如下代码:
var task1 = new Task(() =>
{
Console.WriteLine("Begin");
System.Threading.Thread.Sleep(2000);
Console.WriteLine("Finish");
});
Console.WriteLine("Before start:" + task1.Status);
task1.Start();
Console.WriteLine("After start:" + task1.Status);
task1.Wait();
Console.WriteLine("After Finish:" + task1.Status);
Console.Read();
task1.Status就是输出task的当前状态,其输出结果如下:
可以看到调用Start前的状态是Created,然后等待分配线程去执行,到最后执行完成。
从我们可以得出Task的简略生命周期:
Created:表示默认初始化任务,但是“工厂创建的”实例直接跳过。
WaitingToRun: 这种状态表示等待任务调度器分配线程给任务执行。
RanToCompletion:任务执行完毕。
二、Task的任务控制
Task最吸引人的地方就是他的任务控制了,你可以很好的控制task的执行顺序,让多个task有序的工作。下面来详细说一下:
1、Task.Wait
在上个例子中,我们已经使用过了,task1.Wait();就是等待任务执行完成,我们可以看到最后task1的状态变为Completed。
2、Task.WaitAll
看字面意思就知道,就是等待所有的任务都执行完成,下面我们来写一段代码演示一下:
static void Main(string[] args)
{
var task1 = new Task(() =>
{
Console.WriteLine("Task 1 Begin");
System.Threading.Thread.Sleep(2000);
Console.WriteLine("Task 1 Finish");
});
var task2 = new Task(() =>
{
Console.WriteLine("Task 2 Begin");
System.Threading.Thread.Sleep(3000);
Console.WriteLine("Task 2 Finish");
});
task1.Start();
task2.Start();
Task.WaitAll(task1, task2);
Console.WriteLine("All task finished!");
Console.Read();
}
其输出结果如下:
可以看到,任务一和任务二都完成以后,才输出All task finished!
3、Task.WaitAny
这个用发同Task.WaitAll,就是等待任何一个任务完成就继续向下执行,将上面的代码WaitAll替换为WaitAny,输出结果如下:
4、Task.ContinueWith
就是在第一个Task完成后自动启动下一个Task,实现Task的延续,下面我们来看下他的用法,编写如下代码:
static void Main(string[] args)
{
var task1 = new Task(() =>
{
Console.WriteLine("Task 1 Begin");
System.Threading.Thread.Sleep(2000);
Console.WriteLine("Task 1 Finish");
});
var task2 = new Task(() =>
{
Console.WriteLine("Task 2 Begin");
System.Threading.Thread.Sleep(3000);
Console.WriteLine("Task 2 Finish");
});
task1.Start();
task2.Start();
var result = task1.ContinueWith<string>(task =>
{
Console.WriteLine("task1 finished!");
return "This is task result!";
});
Console.WriteLine(result.Result.ToString());
Console.Read();
}
执行结果如下:
可以看到,task1完成之后,开始执行后面的内容,并且这里我们取得task的返回值。
在每次调用ContinueWith方法时,每次会把上次Task的引用传入进来,以便检测上次Task的状态,比如我们可以使用上次Task的Result属性来获取返回值。我们还可以这么写:
var SendFeedBackTask = Task.Factory.StartNew(() => { Console.WriteLine("Get some Data!"); })
.ContinueWith<bool>(s => { return true; })
.ContinueWith<string>(r =>
{
if (r.Result)
{
return "Finished";
}
else
{
return "Error";
}
});
Console.WriteLine(SendFeedBackTask.Result);
首先输出Get some data,然后执行第二个获得返回值true,最后根据判断返回Finished或error。输出结果:
Get some Data!
Finished
其实上面的写法简化一下,可以这样写:
Task.Factory.StartNew<string>(() => {return "One";}).ContinueWith(ss => { Console.WriteLine(ss.Result);});
输出One,这个可以看明白了吧~
更多ContinueWith用法参见:http://technet.microsoft.com/zh-CN/library/dd321405
5、Task的取消
前面说了那么多Task的用法,下面来说下Task的取消,比如我们启动了一个task,出现异常或者用户点击取消等等,我们可以取消这个任务。
如何取消一个Task呢,我们通过cancellation的tokens来取消一个Task。在很多Task的Body里面包含循环,我们可以在轮询的时候判断IsCancellationRequested属性是否为True,如果是True的话就return或者抛出异常,抛出异常后面再说,因为还没有说异常处理的东西。
下面在代码中看下如何实现任务的取消,代码如下:
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);
token.Register(() =>
{
Console.WriteLine("Canceled");
});
Console.WriteLine("Press enter to cancel task...");
Console.ReadKey();
tokenSource.Cancel();
Console.ReadKey();//这句忘了加,程序退出了,看不到“Abort mission success!“这个提示
这里开启了一个Task,并给token注册了一个方法,输出一条信息,然后执行ReadKey开始等待用户输入,用户点击回车后,执行tokenSource.Cancel方法,取消任务。其输出结果如下:
好了,今天先说道这里,明天继续讲task,接下来该说说task的异常处理和其他的一些用法,如果喜欢可以关注我,一有更新会马上通知你。
作者:雲霏霏
博客地址:http://www.cnblogs.com/yunfeifei/
来源:https://www.cnblogs.com/yunfeifei/p/4106318.html


猜你喜欢
- unity场景加载分为同步加载和异步加载,供大家参考,具体内容如下同步加载 loadScene首先将前置工作做好。创建一个项目工程,然后创建
- 建立Android项目,如果会的话特别简单,不会的话让自己去琢磨也需要一定的时间!小编之后将自己学习Android的经验给大家分享出来!1、
- 目录前言connectTimeout:callTimeout:pingIntervalwriteTimeoutreadTimeout总结前言
- 介绍本文实现的功能有:1、播放音乐2、自定义流星数量、飞行速度、光晕大小、流星大小3、自定义表白话语 运用到的知识点有:GUI:j
- 最近没做项目,重新整理了一个最完整的Mybatis Generator(简称MBG)的最完整配置文件,带详解,再也不用去看EN的User G
- 前言Mybatis是web工程开发中非常常用的数据持久化的框架,通过该框架,我们非常容易的进行数据库的增删改查。数据库连接进行事务提交的时候
- 不得已重新配置,这里记下详细步骤,分享给大家。一、安装jdk,具体步骤如下:1、将jdk-7u4-linux-i586.tar拷贝到linu
- Groovy简介Groovy 是增强 Java 平台的唯一的脚本语言。它提供了类似于 Java 的语法,内置映射(Map)、列表(List)
- 最近做了微信公众号支付的开发,由于是第一次做也摸索了几天的时间,也只是达到了实现功能的水平,并没有太多考虑到性能问题,所以这篇文章比较适合初
- JsonTools.java package com.lihua.json.tools;import net.sf.json.JSONObj
- 虽然jvm可以通过GC自动回收无用的内存,但是代码不好的话仍然存在内存溢出的风险。 一、为什么要了解内存泄露和内存溢出?1、内存泄
- 引言坦白从宽吧,我就是那个花了两天两夜把 1M 图片优化到 100kb 的家伙——王小二!自从因为一
- fopen(打开文件)相关函数 open,fclose表头文件 #include<stdio.h>定义函数 FILE * fop
- 现在很多第三方Launcher((如360Launcher,GoLauncher)带有iphone主题,相信玩Android的人大都知道。本
- idea无法切换分支报错idea拉取项目后,master分支配置完项目基础配置后,生成.iml等文件不受git管理后无法检出其他分支报错如下
- 目录微服务微服务架构SpringCould简介环境搭建1. 创建数据库2. 新建一个子模块api3. 新建一个子模块provider作为服务
- 具体可见http://developer.android.com/tools/debugging/ddms.html。 DDMS为IDE和e
- 本文实例讲述了C#自定义缓存封装类。分享给大家供大家参考。具体如下:这个自定义的C#类封装了部分常用的缓存操作,包括写入缓存,读取缓存,设置
- 本文实例讲述了Android发送xml数据给服务器的方法。分享给大家供大家参考。具体如下:一、发送xml数据:public static v
- 最近该忙的都忙完了,自己自定义一直是个弱项,也一直想整个钟表玩玩,网上看了一圈,学习了不少,下面自己做做自定义首先,制作钟表第一步,肯定是画