异步多线程之入Task详解

Posted 菜鸟厚非

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了异步多线程之入Task详解相关的知识,希望对你有一定的参考价值。

上一篇:异步多线程之入ThreadPool
下一篇:异步多线程之Parallel,待更新


简介

1.0 时代的 Thread 提供了太多太底层功能,在 2.0 时代的 Threadpool 切去了一些无用与不可控的 API ,并提供了线程重用与线程数量限制的功能,3.0 时代的 Task 基于 Threadpool 又做了个补救,增加了多个实用的 API,满足了工作中使用场景。

启动多线程方式

Task 对于启动一个新的线程,提供的 API 还是比较丰富的 Run、New Task、Task Factory 都可以启动一个新的线程

Task.Run

例如:启用一个线程

public static void DoSomethingLong(string name)

    Console.WriteLine($"name DoSomethingLong Start,ThreadId:Thread.CurrentThread.ManagedThreadId,Datetime:DateTime.Now.ToLongTimeString()");

    Console.WriteLine($"name DoSomethingLong End,ThreadId:Thread.CurrentThread.ManagedThreadId,Datetime:DateTime.Now.ToLongTimeString()");


static void Main(string[] args)

    Console.WriteLine($"Main Start,ThreadId:Thread.CurrentThread.ManagedThreadId,Datetime:DateTime.Now.ToLongTimeString()");

    Task.Run(() => DoSomethingLong("张三"));

    Console.WriteLine($"Main End,ThreadId:Thread.CurrentThread.ManagedThreadId,Datetime:DateTime.Now.ToLongTimeString()");

    Console.ReadLine();

New Task

例如:启用一个线程

public static void DoSomethingLong(string name)

    Console.WriteLine($"name DoSomethingLong Start,ThreadId:Thread.CurrentThread.ManagedThreadId,Datetime:DateTime.Now.ToLongTimeString()");

    Console.WriteLine($"name DoSomethingLong End,ThreadId:Thread.CurrentThread.ManagedThreadId,Datetime:DateTime.Now.ToLongTimeString()");


static void Main(string[] args)

    Console.WriteLine($"Main Start,ThreadId:Thread.CurrentThread.ManagedThreadId,Datetime:DateTime.Now.ToLongTimeString()");

    new Task(() => DoSomethingLong("张三")).Start();

    Console.WriteLine($"Main End,ThreadId:Thread.CurrentThread.ManagedThreadId,Datetime:DateTime.Now.ToLongTimeString()");

    Console.ReadLine();

Task Factory

例如:启用一个线程

public static void DoSomethingLong(string name)

    Console.WriteLine($"name DoSomethingLong Start,ThreadId:Thread.CurrentThread.ManagedThreadId,Datetime:DateTime.Now.ToLongTimeString()");

    Console.WriteLine($"name DoSomethingLong End,ThreadId:Thread.CurrentThread.ManagedThreadId,Datetime:DateTime.Now.ToLongTimeString()");


static void Main(string[] args)

    Console.WriteLine($"Main Start,ThreadId:Thread.CurrentThread.ManagedThreadId,Datetime:DateTime.Now.ToLongTimeString()");

    TaskFactory taskFactory = Task.Factory;
    taskFactory.StartNew(() => DoSomethingLong("张三"));

    Console.WriteLine($"Main End,ThreadId:Thread.CurrentThread.ManagedThreadId,Datetime:DateTime.Now.ToLongTimeString()");

    Console.ReadLine();


API、应用场景

什么时候可以用多线程?不要说什么提高效率、异步啊,这都是技术名词没有用的。首先任务可以并发执行,即多任务可以分开,满足了条件后用了并发后再说提升速度、异步啊。

我们以项目经理与开发者们,完成项目开发为例,进行讲解多线程(主线程想象成项目经理,子线程想象成开发人员)。

当项目开始的时候,项目经理需要启动一个项目,做一些准备工作,然后开发人员开始编程

例如:定义 Coding 方法,提给给开发者开项目模块使用,Main 为项目经理角色

public static void Coding(string name,string module)

    Console.WriteLine($"name Coding Start module,ThreadId:Thread.CurrentThread.ManagedThreadId,Datetime:DateTime.Now.ToLongTimeString()");

    Console.WriteLine($"name Coding End  module,ThreadId:Thread.CurrentThread.ManagedThreadId,Datetime:DateTime.Now.ToLongTimeString()");


static void Main(string[] args)

    Console.WriteLine($"项目经理启动一个项目,ThreadId:Thread.CurrentThread.ManagedThreadId,Datetime:DateTime.Now.ToLongTimeString()");
    Console.WriteLine($"前置的准备工作,ThreadId:Thread.CurrentThread.ManagedThreadId,Datetime:DateTime.Now.ToLongTimeString()");
    Console.WriteLine($"开始编程,ThreadId:Thread.CurrentThread.ManagedThreadId,Datetime:DateTime.Now.ToLongTimeString()");

    Task.Run(() => Coding("张三","Web"));
    Task.Run(() => Coding("李四", "Service"));
    Task.Run(() => Coding("王五", "SQL"));

    Console.ReadLine();

启动程序,项目经理(线程 1)做准备工作,开发者们(子线程 3、4、5)并发完成 Coding

WaitAll

例如:当项目开发完成,项目经理需要进行通知甲方验收对吧。所以我们在 Main 方法项目添加通知甲方验收

public static void Coding(string name,string module)

    Console.WriteLine($"name Coding Start module,ThreadId:Thread.CurrentThread.ManagedThreadId,Datetime:DateTime.Now.ToLongTimeString()");

    Console.WriteLine($"name Coding End  module,ThreadId:Thread.CurrentThread.ManagedThreadId,Datetime:DateTime.Now.ToLongTimeString()");


static void Main(string[] args)

    Console.WriteLine($"项目经理启动一个项目,ThreadId:Thread.CurrentThread.ManagedThreadId,Datetime:DateTime.Now.ToLongTimeString()");
    Console.WriteLine($"前置的准备工作,ThreadId:Thread.CurrentThread.ManagedThreadId,Datetime:DateTime.Now.ToLongTimeString()");
    Console.WriteLine($"开始编程,ThreadId:Thread.CurrentThread.ManagedThreadId,Datetime:DateTime.Now.ToLongTimeString()");

    Task.Run(() => Coding("张三","Web"));
    Task.Run(() => Coding("李四", "Service"));
    Task.Run(() => Coding("王五", "SQL"));

    Console.WriteLine($"通知甲方验收,ThreadId:Thread.CurrentThread.ManagedThreadId,Datetime:DateTime.Now.ToLongTimeString()");

    Console.ReadLine();

启动程序,啥情况,开发人员没有 coding 完成,项目经理就通知,甲方验收了。所以,项目经理需要等待开发人员 coding 完成之后,再进行通知甲方验收。
接着我们对程序进行修改,Task 通过了线程等待的方法,不过是 Array 类型的。 我们定义 List 将 个个开发者的任务添加到集合中,使用 WaitAll 方法进行等待全部任务完成。

public static void Coding(string name, string module)

    Console.WriteLine($"name Coding Start module,ThreadId:Thread.CurrentThread.ManagedThreadId,Datetime:DateTime.Now.ToLongTimeString()");

    Console.WriteLine($"name Coding End  module,ThreadId:Thread.CurrentThread.ManagedThreadId,Datetime:DateTime.Now.ToLongTimeString()");


static void Main(string[] args)

    List<Task> tasks = new List<Task>();

    Console.WriteLine($"项目经理启动一个项目,ThreadId:Thread.CurrentThread.ManagedThreadId,Datetime:DateTime.Now.ToLongTimeString()");
    Console.WriteLine($"前置的准备工作,ThreadId:Thread.CurrentThread.ManagedThreadId,Datetime:DateTime.Now.ToLongTimeString()");
    Console.WriteLine($"开始编程,ThreadId:Thread.CurrentThread.ManagedThreadId,Datetime:DateTime.Now.ToLongTimeString()");

    tasks.Add(Task.Run(() => Coding("张三", "Web")));
    tasks.Add(Task.Run(() => Coding("李四", "Service")));
    tasks.Add(Task.Run(() => Coding("王五", "SQL")));

    Task.WaitAll(tasks.ToArray()); // 会阻塞当前线程,所有任务完成后,才进入下一行 卡界面

    Console.WriteLine($"通知甲方验收,ThreadId:Thread.CurrentThread.ManagedThreadId,Datetime:DateTime.Now.ToLongTimeString()");

    Console.ReadLine();

启动程序,可以看到这次在开发者们(子线程 3、4、6)完成 Coding 后,项目经理(主线程 1)再通知甲方进行验收

WaitAll 等待也可以超时

WaitAny

例如:有时项目经理不知道开发项目的进度,于是在项目中任何一个模块开发完成后进行记录。Task提供了对应得 API WaitAny 方法,即任何一个子线程完成工作即开始下一行代码。

public static void Coding(string name, string module)

    Console.WriteLine($"name Coding Start module,ThreadId:Thread.CurrentThread.ManagedThreadId,Datetime:DateTime.Now.ToLongTimeString()");

    Console.WriteLine($"name Coding End  module,ThreadId:Thread.CurrentThread.ManagedThreadId,Datetime:DateTime.Now.ToLongTimeString()");


static void Main(string[] args)

    List<Task> tasks = new List<Task>();

    Console.WriteLine($"项目经理启动一个项目,ThreadId:Thread.CurrentThread.ManagedThreadId,Datetime:DateTime.Now.ToLongTimeString()");
    Console.WriteLine($"前置的准备工作,ThreadId:Thread.CurrentThread.ManagedThreadId,Datetime:DateTimeC#/.NET 多线程任务Task的详解——应用实例

异步和多线程,委托异步调用,Thread,ThreadPool,Task,Parallel,CancellationTokenSource

异步多线程Task

C# Task总结(异步操作+并行)

Task和async/await详解

详解.NET 异步编程,如何透过现象,看async/await本质