C#并行任务库(TPL)中Task详解

1、Task介绍

1.1 Task产生背景

Task出现之前,微软的多线程处理方式有:Thread→ThreadPool→委托的异步调用,虽然也可以满足基本业务需要的多线程场景,但它们在多个线程的等待处理方面、资源占用方面、线程延续和阻塞方面、线程的取消方面等都显得比较笨拙,在面对复杂的业务场景下,显得有点捉襟见肘了。

ThreadPool相比Thread来说具备了很多优势,但是ThreadPool却又存在一些使用上的不方便。比如:

  • ThreadPool不支持线程的取消、完成、失败通知等交互性操作;
  • ThreadPool不支持线程执行的先后次序;

正是在这种背景下,Task应运而生。Task是微软在.Net 4.0时代推出来的,也是微软极力推荐的一种多线程的处理方式,Task看起来像一个Thread,实际上,它是在ThreadPool的基础上进行的封装,Task的控制和扩展性很强,在线程的延续、阻塞、取消、超时等方面远胜于Thread和ThreadPool。

1.2 Task 原理

这里简要的分析下CLR线程池,其实线程池中有一个叫做“全局队列”的概念,每一次我们使用QueueUserWorkItem的使用都会产生一个“工作项”,然后“工作项”进入“全局队列”进行排队,最后线程池中的的工作线程以FIFO(First In First Out,即先入先出)的形式取出,这里值得一提的是在.net 4.0之后“全局队列”采用了无锁算法,相比以前版本锁定“全局队列”带来的性能瓶颈有了很大的改观。那么任务委托的线程池不光有“全局队列”,而且每一个工作线程都有”局部队列“。我们的第一反应肯定就是“局部队列“有什么好处呢?这里暂且不说,我们先来看一下线程池中的任务分配,如下图:
以下是一个简单的任务示例:
在这里插入图片描述
线程池的工作方式大致如下,线程池的最小线程数是6,线程1~3正在执行任务1~3,当有新的任务时,就会向线程池请求新的线程,线程池会将空闲线程分配出去,当线程不足时,线程池就会创建新的线程来执行任务,直到线程池达到最大线程数(线程池满)。总的来说,只有有任务就会分配一个线程去执行,当FIFO十分频繁时,会造成很大的线程管理开销。

下面我们来看一下task中是怎么做的,当我们new一个task的时候“工作项”就会进去”全局队列”,如果我们的task执行的非常快,那么“全局队列“就会FIFO的非常频繁,那么有什么办法缓解呢?当我们的task在嵌套的场景下,“局部队列”就要产生效果了,比如我们一个task里面有3个task,那么这3个task就会存在于“局部队列”中,如下图的任务一,里面有三个任务要执行,也就是产生了所谓的"局部队列",当任务三的线程执行完成时,就会从任务队列中以FIFO的形式"窃取"任务执行,从而减少了线程管理的开销。这就相当于,有两个人,一个人干完了分配给自己的所有活,而另一个人却还有很多的活,闲的人应该接手忙的人的活,一起快速完成。
在这里插入图片描述 从上面的分析我们看到,这些分流和负载都是普通ThreadPool.QueueUserWorkItem不能办到的,所以说在.net 4.0之后,我们尽可能的使用TPL(Task Parallel Library,即并行任务库),抛弃ThreadPool。

static void Main(string[] args)
{
   
    Task t = new Task(() =>
    {
   
        Console.WriteLine("任务开始工作……");
        Thread.Sleep(5000);  // 替换为具体业务处理
    });
    t.Start();
    t.ContinueWith(task =>
    {
   
        Console.WriteLine("任务完成,完成时候的状态为:");
        Console.WriteLine("IsCanceled={0}\tIsCompleted={1}\tIsFaulted={2}", 
                          task.IsCanceled, task.IsCompleted, task.IsFaulted);
    });
    Console.ReadKey();
}

2、Task使用方法

2.1 创建和启动任务

2.1.1 无返回值的方式

  • 方式1:new方式实例化一个Task,需要通过Start方法启动
static void TaskMethod(string name)
{
   
    Console.WriteLine("Task {0} is running on a thread id {1}. Is thread pool thread: {2}",
                      name, Thread.CurrentThread.ManagedThreadId, 
                      Thread.CurrentThread.IsThreadPoolThread);
}
var t= new Task(() => TaskMethod("Task 1"));
task.Start();
Task.WaitAll(t);	// 等待所有任务结束 

任务的状态:Start之前为Created,之后为WaitingToRun

  • 方式2:静态方法Task.Run(Action action)将任务放在线程池队列,返回并启动一个Task
Task.Run(() => TaskMethod("Task 2"));
  • 方式3:任务工厂创建和启动一个Task
// TaskFactory工厂
TaskFactory taskFactory = new TaskFactory() ;
taskFactory.StartNew(() => TaskMethod("Task 3"));
// Task.Factory属性
Task.Factory.StartNew(() => TaskMethod("Task 3"));
// 其他
var t = Task.Factory.StartNew(() => TaskMethod("Task 3"));
Task.WaitAll(t);

任务的状态:Start之前为Running,之后为Running


static void Main(string[] args)
{
   
    var t1 = new Task(() => TaskMethod("Task 1"));
    var t2 = new Task(() => TaskMethod("Task 2"));
    t1.Start();
    t2.Start();
    Task.WaitAll(t1, t2);
    Task.Run(() => TaskMethod("Task 3"));
    Task.Factory.StartNew(() => TaskMethod("Task 4"));
    // 标记为长时间运行任务,此时任务不会使用线程池,而在单独的线程中运行。
    Task.Factory.StartNew(() => TaskMethod("Task 5"), TaskCreationOptions.LongRunning);
    
    #region 常规的使用方式
    Console.WriteLine("主线程执行业务处理.");
    // 创建任务
    Task task = new Task(() =>
                         {
   
                             Console.WriteLine("使用`System.Threading.Tasks.Task`执行异步操作.");
                             for (int i = 0; i < 10; i++)
                             {
   
                                 Console.WriteLine(i);
                             }
                         });
    // 启动任务,并安排到当前任务队列线程中执行任务(System.Threading.Tasks.TaskScheduler)
    task.Start();
    Console.WriteLine("主线程执行其他处理");
    task.Wait();
    #endregion

    Thread.Sleep(TimeSpan.FromSeconds(1));
    Console.ReadLine();
}

static void TaskMethod(string name)
{
   
    Console.WriteLine("Task {0} is running on a thread id {1}. Is thread pool thread: {2}",
                      name, Thread.CurrentThread.ManagedThreadId, 
                      Thread.CurrentThread.IsThreadPoolThread);
}
  • 方式4:RunSynchronously同步启动
    Task实例化后调用同步方法RunSynchronously,进行线程启动。(备注: 类似委托开启线程,BeginInvoke是异步,而Invoke是同步)
var task = new Task(() => TaskMethod("Task 1"));
task.RunSynchronously();
  • 方式5:async/await的实现方式
using System;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp1
{
   
    class Program
    {
   
        async static void AsyncFunction()
        {
   
            await Task.Delay(1);
            Console.WriteLine("使用`System.Threading.Tasks.Task`执行异步操作.");
            for (int i = 0; i < 10; i++)
            {
   
                Console.WriteLine(string.Format("AsyncFunction:i={0}", i));
            }
        }

        public static void Main()
        {
   
            Console.WriteLine("主线程执行业务处理.");
            AsyncFunction();
            Console.WriteLine("主线程执行其他处理");
            for (int i = 0; i < 10; i++)
            {
   
                Console.WriteLine(string.Format("Main:i={0}", i));
            }
            Console.ReadLine();
        }
    }
}

2.1.2 带返回值的方式

方式4:

Task<int> task = CreateTask("Task 1");
task.Start(); 
int result = task.Result;

using System;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp1
{
   
    class Program
    {
   
        static int TaskMethod(string name)
        {
   
            Console.WriteLine("Task {0} is running on a thread id {1}. Is thread pool thread: {2}",
                name, Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread);
            Thread.Sleep(TimeSpan.FromSeconds(2));
            return 1;
        }
        
        static Task<int> CreateTask(string name)
        {
   
            return new Task<int>(() => TaskMethod(name));
        }

        static void Main(string[] args)
        {
   
            TaskMethod("主线程任务");
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值