首页 星云 工具 资源 星选 资讯 热门工具
:

PDF转图片 完全免费 小红书视频下载 无水印 抖音视频下载 无水印 数字星空

多线程、任务、异步的区别

编程知识
2024年09月03日 21:54

Task和Thread的区别

这是一个高频,深刻的问题,无论去哪都逃不过被询问这个问题。Task是基于Thread的,这是众所周知的。但是Task和Thread的联系如此简单和纯粹确实我没想到的。甚至只需要几十行代码就能呈现其原理。一个简单的模拟实例说明Task及其调度问题,这真是一篇好文章。

任务体系由两个类组成,Task,以及TaskScheduler

Task储存需要到多线程去执行的委托方法,尽管经过层层封装,内部最终还是调用这个委托。但是任务的执行方法不向程序员开放,而是交给了TaskScheduler,暴露给程序员的只有把任务交给任务调度器这个方法。任务说白了是围绕委托这个中心构建的。至于委托在哪个线程上执行,职责不在此,交给了任务调度器。

TaskScheduler用于决定将Task放到哪个线程上执行,最简单的是new Thread将Task及其内部的委托放进新线程去执行。复杂一点的就是调用线程池的排队方法,将Task放到线程池要访问的待执行Task队列中,让队列不断弹出Task,然后放到某个线程中去执行。

实现两个TaskScheduler

我举两个任务调度器来说明这有多简单。一个调度器用于对每个任务创建一个线程执行,另一个调度器用于创建一个线程池,并用线程池的线程去取任务执行。

  • 总是使用新线程执行任务
//使用新线程执行任务
public class ThreadScheduler:TaskScheduler
{
	protected override void QueueTask(Task task)
	{
		//没想到就是直接把Task放到Thread中去执行了
		new Thread(()=>TryExecuteTask(task))
		.Start();
	}
}

//测试
ThreadScheduler threadScheduler = new ThreadScheduler();
Task.Factory.StartNew(() => Console.WriteLine($"Task1 is excuted in thread {Thread.CurrentThread.ManagedThreadId}"), default, TaskCreationOptions.None, threadScheduler);
Task.Factory.StartNew(() => Console.WriteLine($"Task2 is excuted in thread {Thread.CurrentThread.ManagedThreadId}"), default, TaskCreationOptions.None, threadScheduler);
Task.Factory.StartNew(() => Console.WriteLine($"Task3 is excuted in thread {Thread.CurrentThread.ManagedThreadId}"), default, TaskCreationOptions.None, threadScheduler);
Task.Factory.StartNew(() => Console.WriteLine($"Task4 is excuted in thread {Thread.CurrentThread.ManagedThreadId}"), default, TaskCreationOptions.None, threadScheduler);

image

  • 创建并使用线程池执行任务
//使用线程池执行任务
public class ThreadPoolScheduler:TaskScheduler
{
	private BlockingCollection<Task> tasks=new();
	private Thread[] threads;
	
	//创建一个线程池,让线程不断去队列中取出任务执行
	public ThreadPoolScheduler(int threadNum)
	{
		threads=new Thread[threadNum];
		for(int i=0;i<threadNum;i++)
		{
			threads[i]=new Thread(InvokeNext);
			threads[i].Start();
		}
		void InvokeNext()
		{
			while(true)
			{
				var task=tasks.Take();
				if(task!=null)
				{
					TryExecuteTask(task);
				}
			}
		}
	}

	//新任务入队
	protected override void QueueTask(Task task)
	{
		tasks.Add(task);
	}
}


//测试
ThreadPoolScheduler threadScheduler = new ThreadPoolScheduler(2);
Task.Factory.StartNew(() => Console.WriteLine($"Task1 is excuted in thread {Thread.CurrentThread.ManagedThreadId}"), default, TaskCreationOptions.None, threadScheduler);
Task.Factory.StartNew(() => Console.WriteLine($"Task2 is excuted in thread {Thread.CurrentThread.ManagedThreadId}"), default, TaskCreationOptions.None, threadScheduler);
Task.Factory.StartNew(() => Console.WriteLine($"Task3 is excuted in thread {Thread.CurrentThread.ManagedThreadId}"), default, TaskCreationOptions.None, threadScheduler);
Task.Factory.StartNew(() => Console.WriteLine($"Task4 is excuted in thread {Thread.CurrentThread.ManagedThreadId}"), default, TaskCreationOptions.None, threadScheduler);

image

从这两种调度器可以看出,开始一个任务这个动作是唯一明确的只有一点,就是把任务交给调度器,而不是立即执行任务。至于任务有没有立即被放到线程中执行,这却决于任务调度器的实现。比如在第一种调度器中,任务被立即执行;在第二种调度器中,任务可能会等待,直到有空闲线程把它从队列中取出来。

回调

任务和多线程还有一个区别是拥有回调ContinueWith。这样就不需要使用阻塞或线程同步去解决这种很常见的,在一件事完成后再做另一件事的问题。大内老A提出的方式是,在任务内部,在执行委托的那个函数中,在前一个委托执行完成后,开启一个新任务,执行下一个委托。
由于这个触发节点在前一个线程即将结束时,所以能实现回调。
由于回调和开始任务这两个方法有相同返回类型Task,所以又实现了链式调用。

异步

异步的解释是以同步的方式进行异步编程。这是对任务的进一步改进。这玩意只能通过语法糖去实现,达到的效果是将任务的回调执行模型变换为了直观上看起来的顺序执行模型。在多线程同步这个问题上,可以得出这样一条演变链条。

Thread Task async/await
锁,信号量等线程同步 回调 同步的方式编程

异步和多线程有什么区别?主要在于线程同步方式的区别吧。

From:https://www.cnblogs.com/ggtc/p/18392358
本文地址: http://shuzixingkong.net/article/1711
0评论
提交 加载更多评论
其他文章 英语疑问句倒装特征以及从句的正常语序
上一篇介绍了英文中的五大基本句型,在进一步介绍句子如何在五大句型之上如何各种添枝加叶变得复杂之前,我们先来搞懂英语疑问句倒装特征以及从句的正常语序。 这是至关重要的一件事,但其实太多初学者都给忽略了,在懵圈中苦学了很久,结果在实际应用时产生了各种混乱,事倍而功半。 本文想通过举例的方式,来试图讲清这
英语疑问句倒装特征以及从句的正常语序
搜索组件优化 - Command ⌘K
今天心血来潮想在 `blog` 上找一篇文章,用搜素的功能发现搜不出来😂,搜索挂了?然后突然想起来之前由于想着在 `blog` 中可能加一些私有的配置或者尝鲜的功能,所有 `fork` 了一份变成 私有项目了,这样就不符合 `DocSearch` 的 网站必须是公开的这个限制了。
搜索组件优化 - Command ⌘K 搜索组件优化 - Command ⌘K 搜索组件优化 - Command ⌘K
C#自定义控件—文本显示、文本设值
C#用户控件之文本显示、设定组件 如何绘制一个便捷的文本显示组件、文本设值组件(TextShow,TextSet)? 绘制此控件的目的就是方便一键搞定标签显示(可自定义方法显示文本颜色等),方便自定义方法又省略了挨个拖拽的过程 纯定义属性 【文本设定】:字体、标签、值、单位;事件方法:Enter、L
C#自定义控件—文本显示、文本设值
CMake构建学习笔记14-依赖库管理工具
如果说做C/C++开发最大的痛点是什么,那么一定是缺少一个官方的统一的包管理器。认真的说,如果你要用C/C++干点什么,至少需要(Windows系统下): C/C++语言本身、标准库、以及操作系统API几乎干不了什么,除非你真的想从零开始造轮子。 开始找一些现成的实现组成依赖库。最好看能不能找到预编
论文速读纪录 - 202408
特别鸣谢kimi,以下论文均在kimi辅助下阅读。 目录 RMIB: Representation Matching Information Bottleneck for Matching Text Representations AttentionRank: Unsupervised keyphr
论文速读纪录 - 202408 论文速读纪录 - 202408 论文速读纪录 - 202408
【题目全解】ACGO挑战赛#8
前言:本次挑战赛的难度相较于前面几期有所提升,主要还是因为集训的关系,出题组的成员们没有充裕的时间想原创题目(so,只能原模原样搬运某一场 ABC 的考试了。)Anyway,AK 了就行。 备注:由于 Python 的常数过大,本题解暂不同步更新 Python 版本的题解。 第一题 - Inters
利用分布式锁在ASP.NET Core中实现防抖
前言 在 Web 应用开发过程中,防抖(Debounce) 是确保同一操作在短时间内不会被重复触发的一种有效手段。常见的场景包括防止用户在短时间内重复提交表单,或者避免多次点击按钮导致后台服务执行多次相同的操作。无论在单机环境中,还是在分布式系统中都有一些场景需要使用它。本文将介绍如何在ASP.NE
利用分布式锁在ASP.NET Core中实现防抖
借助表格技术提升智能审计云平台应用体验
背景说明: 2009年,立信加入全球第五大国际会计网络——BDO国际。BDO 全球各地成员所均使用统一的审计方式,在完全遵守国际审计准则的原则同时,也会应不同地区要求提供附加指引,确保在全球提供一致的服务水平。如今,立信的审计过程及档案记录均已实现电子化。然而,审计工作并非一成不变,为了给客户提供最
借助表格技术提升智能审计云平台应用体验 借助表格技术提升智能审计云平台应用体验 借助表格技术提升智能审计云平台应用体验