对 C# 开发者来说,不可不理解清楚 Thread、ThreadPool 和 Task 这三个概念。这也是面试频率很高的话题,在 StackOverflow 可以找到有很多不错的回答,我总结整理了一下。

Thread

Thread 是一个实际的操作系统级别的线程(OS 线程),有自己的栈和内核资源。Thread 允许最高程度的控制,你可以 Abort、Suspend 或 Resume 一个线程,你还可以监听它的状态,设置它的堆栈大小和 Culture 等属性。Thread 的开销成本很高,你的每一个线程都会为它的堆栈消耗相对较多的内存,并且在线程之间的处理器上下文切换时会增加额外的 CPU 开销。

ThreadPool

ThreadPool(线程池)是一堆线程的包装器,由 CLR 维护。你对线程池中的线程没有任何控制权,你甚至无法知道线程池什么时候开始执行你提交的任务,你只能控制线程池的大小。简单来说,线程池调用线程的机制是,它首先调用已创建的空闲线程来执行你的任务,如果当前没有空闲线程,可能会创建新线程,也可能会等待。

使用 ThreadPool 可以避免创建太多线程的开销。但是,如果你向 ThreadPool 提交了太多长时间运行的任务,它可能会被填满,这时你提交的后面的任务可能最终会等待前面的长时间运行的任务执行完成。此外,线程池没有提供任何方法来检测一个工作任务何时完成(不像 Thread.Join()),也没有方法来获取结果。因此,ThreadPool 最好用于调用者不需要结果的短时操作。

Task

Task 是 TPL(Task Parallel Library)提供一个类,它在 Thread 和 TheadPool 之间提供了两全其美的解决方案。和 ThreadPool 一样,Task 并不创建自己的OS 线程。相反,Task 是由 TaskScheduler 调度器执行的,默认的调度器只是在 ThreadPool 上运行。

与 ThreadPool 不同的是,Task 还允许你知道它完成的时间,并获取返回一个结果。你可以在现有的 Task 上调用 ContinueWith(),使它在任务完成后运行更多的代码(如果它已经完成,就会立即运行回调)。

你也可以通过调用 Wait() 来同步等待一个任务的完成(或者,通过获取它的 Result 属性)。与 Thread.Join() 一样,这将阻塞调用线程,直到任务完成。通常不建议同步等待任务执行完成,它使调用线程无法进行任何其他工作。如果当前线程要等待其它线程任务执行完成,建议使用 async/await 异步等待,这样当前线程可以空闲出来去处理其它任务,比如在 await Task.Delay() 时,并不占用线程资源。

由于任务仍然在 ThreadPool 上运行,因此不应该将其用于长时任务的执行,因为它们会填满线程池并阻塞新的工作任务。相反,Task 提供了一个 LongRunning 选项,它将告诉 TaskScheduler 启用一个新的线程,而不是在 ThreadPool 上运行。

所有较新的上层多线程 API,包括 Parallel.ForEach()、PLINQ、async/await 等,都是建立在 Task 上的。

Thread 和 Task 简单示例

下面通过一个简单示例演示 Thread 和 Task 的使用,注意他们是如何创建、传参、执行和等待执行完成的。

static void Main(string[] args)
{
// 创建两个新的 Thread
var thread1 = new Thread(new ThreadStart(() => PerformAction("Thread", 1)));
var thread2 = new Thread(new ThreadStart(() => PerformAction("Thread", 2))); // 开始执行线程任务
thread1.Start();
thread2.Start(); // 等待两个线程执行完成
thread1.Join();
thread2.Join(); Console.WriteLine("Theads done!"); Console.WriteLine("===我是分隔线==="); // 创建两个新的 Task
var task1 = Task.Run(() => PerformAction("Task", 1));
var task2 = Task.Run(() => PerformAction("Task", 2)); // 执行并等待两个 Task 执行完成
Task.WaitAll(new[] { task1, task2 }); Console.WriteLine("Tasks done!"); Console.ReadKey();
} static void PerformAction(string threadOrTask, int id)
{
var rnd = new Random(id);
for (int i = 0; i < 5; i++)
{
Console.WriteLine($"{threadOrTask}: {id}: {i}", id, i);
Thread.Sleep(rnd.Next(0, 1000));
}
}

运行效果:

注意到,相比之下 Task 比 Thread 好用得多,加上前文 Task 和 Thread 的对比,对我们编码的指导意义是:大多数情况我们应该使用 Task,而不要直接使用 Thread,除非你明确知道你需要一个独立的线程来执行一个长耗时的任务。

Thread、ThreadPool 和 Task的更多相关文章

  1. 从Thread,ThreadPool,Task, 到async await 的基本使用方法解读

    记得很久以前的一个面试场景: 面试官:说说你对JavaScript闭包的理解吧? 我:嗯,平时都是前端工程师在写JS,我们一般只管写后端代码. 面试官:你是后端程序员啊,好吧,那问问你多线程编程的问题 ...

  2. .NET 异步多线程,Thread,ThreadPool,Task,Parallel,异常处理,线程取消

    今天记录一下异步多线程的进阶历史,以及简单的使用方法 主要还是以Task,Parallel为主,毕竟用的比较多的现在就是这些了,再往前去的,除非是老项目,不然真的应该是挺少了,大概有个概念,就当了解一 ...

  3. Thread,ThreadPool,Task

    线程分为前台和后台.比如我们直接new一个Thread这就是前台线程. 前台线程一定会执行. 比如我们创建2个线程:1号,2号,同时执行,假设1号是主线程,1执行完了,依旧会等待2执行完成,整个程序才 ...

  4. c#中@标志的作用 C#通过序列化实现深表复制 细说并发编程-TPL 大数据量下DataTable To List效率对比 【转载】C#工具类:实现文件操作File的工具类 异步多线程 Async .net 多线程 Thread ThreadPool Task .Net 反射学习

    c#中@标志的作用   参考微软官方文档-特殊字符@,地址 https://docs.microsoft.com/zh-cn/dotnet/csharp/language-reference/toke ...

  5. C# Thread、ThreadPool、Task、Invoke、BeginInvoke、async、await 汇总

    本文将主要通过"同步调用"."异步调用"."异步回调"三个示例来讲解在用委托执行同一个"加法类"的时候的的区别和利弊. ...

  6. Thread,ThreadPool,Task, 到async await 的基本使用方法和理解

    很久以前的一个面试场景: 面试官:说说你对JavaScript闭包的理解吧? 我:嗯,平时都是前端工程师在写JS,我们一般只管写后端代码. 面试官:你是后端程序员啊,好吧,那问问你多线程编程的问题吧. ...

  7. 异步多线程 Thread ThreadPool Task

    一.线程 Thread ThreadPool 线程是Windows任务调度的最小单位,线程是程序中的一个执行流,每个线程都有自己的专有寄存器(栈指针.程序计数器等),但代码区是共享的,即不同的线程可以 ...

  8. NET 异步多线程,THREAD,THREADPOOL,TASK,PARALLEL

    .NET 异步多线程,THREAD,THREADPOOL,TASK,PARALLEL,异常处理,线程取消 今天记录一下异步多线程的进阶历史,以及简单的使用方法 主要还是以Task,Parallel为主 ...

  9. .NET异步多线程,Thread,ThreadPool,Task,Parallel,异常处理,线程取消

    今天记录一下异步多线程的进阶历史,以及简单的使用方法 主要还是以Task,Parallel为主,毕竟用的比较多的现在就是这些了,再往前去的,除非是老项目,不然真的应该是挺少了,大概有个概念,就当了解一 ...

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

    1 进程-线程-多线程,同步和异步2 异步使用和回调3 异步参数4 异步等待5 异步返回值 5 多线程的特点:不卡主线程.速度快.无序性7 thread:线程等待,回调,前台线程/后台线程, 8 th ...

随机推荐

  1. Xamarin/Unity3d无法访问Azure服务器或者微软API

    Xamarin因为是mono项目的商用版,mono项目是.net技术的开源修改版,所以和微软的服务对接时候会出现安全验证问题. mono项目本质是对汇编级的中间语言二次编译.可参考公共语言运行时相关知 ...

  2. nginx多ip多端口多域名方式

    目录 一:Nginx虚拟主机 1.基于ip的方式 2.基于多端口的方式 3.基于多域名的方式 一:Nginx虚拟主机 基于多IP的方式 基于多端口的方式 基于多域名的方式 1.基于ip的方式 [roo ...

  3. Codeforces Round #742 (Div. 2)

    A. Domino Disaster 思路 按照题意模拟即可 如果是 对应关系为R --> R L --> L U --> D D --> U AC_CODE inline v ...

  4. AI算法测评(二)--算法测试流程

    根据算法测试过程中遇到的一些问题和管理规范, 梳理出算法测试工作需要关注的一些点: 编号 名称 描述信息 备注 1 明确算法测试需求 明确测试目的 明确测试需求, 确认测试需要的数据及场景 明确算法服 ...

  5. 「CTSC2006」歌唱王国

    概率生成函数\(g(x)=\sum_{i\geq 0}t_ix^i\),\(t_i\)表示结果为\(i\)的概率 令\(f(x)\)表示i位表示串结束时长度为i的概率,\(G(x)\)表示i位表示串长 ...

  6. MAC上安装HEAAN库

    介绍 HEAN是一个软件库,它实现支持定点运算的同态加密(HE),此库支持有理数之间的近似运算.近似误差取决于某些参数,与浮点运算误差几乎相同.该库中的方案发表在"近似数算术的同态加密&qu ...

  7. SQLServer、Mysql、Oracle 创建、删除用户和授予用户权限

    SQLServer 1.创建用户 CREATE LOGIN [用户名称] WITH PASSWORD='用户密码', DEFAULT_DATABASE=[默认数据库名称], CHECK_EXPIRAT ...

  8. DelayQueue延迟队列-实现缓存

    延迟阻塞队列DelayQueue DelayQueue 是一个支持延时获取元素的阻塞队列, 内部采用优先队列 PriorityQueue 存储元素, 同时元素必须实现 Delayed 接口:在创建元素 ...

  9. Kubernetes:健康检查

    Blog:博客园 个人 应用在运行过程中难免会出现错误,如程序异常.软件异常.硬件故障.网络故障等.因此,系统通过一些手段来判断应用是否运行正常,这些手段称之为健康检查(诊断). 前置知识 回顾一下P ...

  10. 1、架构--架构图、Iptables(简介、四表五链、流程图、使用、扩展模块)、包过滤防火墙

    笔记 1.画架构图 2.Iptables 1.1 什么是防火墙 防止别人恶意访问. 1.2 防火墙种类 硬件防火墙 F5 软件防火墙 iptables firewalld 安全组 3.Iptables ...