前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Thread、ThreadPool、Task、Parallel、Async和Await基本用法、区别以及弊端

Thread、ThreadPool、Task、Parallel、Async和Await基本用法、区别以及弊端

作者头像
AI.NET 极客圈
修改于 2025-04-09 06:15:33
修改于 2025-04-09 06:15:33
1.9K00
代码可运行
举报
文章被收录于专栏:AI.NET极客圈AI.NET极客圈
运行总次数:0
代码可运行

多线程的操作在程序中也是比较常见的,比如开启一个线程执行一些比较耗时的操作(IO操作),而主线程继续执行当前操作,不会造成主线程阻塞。线程又分为前台线程和后台线程,区别是:整个程序必须要运行完前台线程才会退出,而后台线程会在程序退出的时候结束掉。Thread默认创建的是前台线程,而ThreadPool和Task默认创建的是后台线程,Thread可以通过设置 IsBackground 属性将线程设置为后台线程。

前台线程

效果:

后台线程

效果:

下面来说一下几种开启多线程的方法:

1、Thread

1.1 开启一个线程,执行一个不带参数的方法

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
static void Main(string[] args)
{
    Thread thread = new Thread(new ThreadStart(NoParameterMethod));
    //注意Start开启线程之后,当前线程不是说一定会立马执行
    //而是说当前线程已经准备好被CPU调用,至于CPU什么时候调用是需要看情况而定
    thread.Start();
    Console.WriteLine("程序已经执行完成");
}

static void NoParameterMethod()
{
    //使当前线程停止1s
    Thread.Sleep(1000);
    Console.WriteLine("NoParameterMethod");
}

1.2开启一个线程,执行带参数的方法

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
static void Main(string[] args)
{
    Thread thread = new Thread(new ParameterizedThreadStart(ParameterMethod));
    //要传入的参数在Start的时候传入
    thread.Start("ParameterMethod");
    Console.WriteLine("程序已经执行完成");
}
//参数类型必须为Object类型,方法只能有一个参数
//如果想传入多个参数,可已将参数封装进入一个类中
static void ParameterMethod(Object x) {
    Thread.Sleep(1000);
    Console.WriteLine(x);
}

2、ThreadPool

使用ThreadPool开启一个线程

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//无参    Thread.CurrentThread.ManagedThreadId是当前线程的唯一标识符
ThreadPool.QueueUserWorkItem(new WaitCallback(obj => Console.WriteLine(Thread.CurrentThread.ManagedThreadId)));
//有参
ThreadPool.QueueUserWorkItem(new WaitCallback(obj => Console.WriteLine(Thread.CurrentThread.ManagedThreadId)), "参数");

ThreadPool是Thread的一个升级版,ThreadPool是从线程池中获取线程,如果线程池中又空闲的元素,则直接调用,如果没有才会创建,而Thread则是会一直创建新的线程,要知道开启一个线程就算什么事都不做也会消耗大约1m的内存,是非常浪费性能的,接下来我们写一个例子来看一下二者的区别:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#region 使用Thread开启100个线程
for (int i = 0; i < 100; i++)
{
    (new Thread(new ThreadStart(() => Console.WriteLine(Thread.CurrentThread.ManagedThreadId)))).Start();
}
#endregion

运行结果:

我们可以看到每一个主线程表示id都是不同的,也就是说使用Thread开启线程每次都是新创建一个

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#region 使用ThreadPool开启100个线程
for (int i = 0; i < 100; i++)
{
    ThreadPool.QueueUserWorkItem(new WaitCallback(obj => Console.WriteLine(Thread.CurrentThread.ManagedThreadId)));
}
#endregion

运行结果:

相信区别已经很明显了,这里我再说一下,线程池中一开始是没有一个线程的,使用ThreadPool开启一个线程之后,线程执行完毕,会加入到线程池中,后续需要再次开启线程的时候查看线程池中有没有空闲的线程,有则调用,没有则创建,如此循环

二者之间还有一个区别,就是ThreadPool可以操控线程的状态,比如等待线程完成,或者终止超时子线程操作

取消子线程操作

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
CancellationTokenSource cts = new CancellationTokenSource();
ThreadPool.QueueUserWorkItem(new WaitCallback(CanCancelMethod),cts.Token);
cts.Cancel();
Console.ReadKey();
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
static void CanCancelMethod(Object obj) {
    CancellationToken ct = (CancellationToken)obj;
    if (ct.IsCancellationRequested) {
        Console.WriteLine("该线程已取消");
    }
    //就算ct.IsCancellationRequested为真,接下来的代码还是会执行
    //因为该方法并没有ruturn
    Thread.Sleep(1000);
    Console.WriteLine($"子线程{Thread.CurrentThread.ManagedThreadId}结束");
}

感觉这个取消子线程的方法和设置一个全局变量,然后通过判断和更改全局变量的值,设置线程是否取消的效果一样

ThreadPool的其他操作感兴趣的可以自己搜索学一下,因为终止线程什么操作都是比较麻烦的,关于ThreadPool就不再多说了

3、Task

Task和ThreadPool是一样的,都是从线程池中取空闲的线程

使用Task开启一个线程

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//方法1  使用Task的Run方法
Task.Run(()=> {
    Console.WriteLine($"线程{Thread.CurrentThread.ManagedThreadId}已开启");
});
//方法2   使用Task工厂类TaskFactory对象的StartNew方法
(new TaskFactory()).StartNew(() =>
{
    Console.WriteLine($"线程{Thread.CurrentThread.ManagedThreadId}已开启");
});

Run和StartNew方法都是返回一个Task类型的对象,代表当前开启的线程,如果方法有返回值

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//如果方法有返回值
Task<int> t1 = Task.Run<int>(() => {
    return 1;
});
//通过t1.Result查看返回的结果
Console.WriteLine(t1.Result);

取消线程操作的话和ThreadPool取消线程操作一样

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//1s后自动取消线程
CancellationTokenSource cts = new CancellationTokenSource(1000);
//为取消线程注册回调函数
cts.Token.Register(()=> {
    Console.WriteLine("线程已取消");
});

Task.Run(()=> {
    Console.WriteLine("开始执行");
    Thread.Sleep(2000);
    //判断当前线程是否已被取消
    if (cts.Token.IsCancellationRequested) {
        Console.WriteLine("方法已结束");
        return;
    }
    Console.WriteLine("线程继续执行");
},cts.Token);

等待所有线程执行完毕

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//存放所有线程
List<Task> lst = new List<Task>();
//开启10个线程
for (int i = 0;i < 10;i++) {
    lst.Add(Task.Run(()=> {
        Thread.Sleep(new Random().Next(1,3000));
        Console.WriteLine($"线程{Thread.CurrentThread.ManagedThreadId}");
    }));
}
//等待所有线程执行完毕
Task.WaitAll(lst.ToArray());
Console.WriteLine("所有线程执行完毕");

等待任意一个先线程执行完毕

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//存放所有线程
List<Task> lst = new List<Task>();
//开启10个线程
for (int i = 0;i < 10;i++) {
    lst.Add(Task.Run(()=> {
        Thread.Sleep(new Random().Next(1,3000));
        Console.WriteLine($"线程{Thread.CurrentThread.ManagedThreadId}");
    }));
}
//等待任意线程执行完毕
Task.WaitAny(lst.ToArray());
Console.WriteLine("已有现成执行完毕");

对于Thread、ThreadPool和Task,如果要用多线程的话,优先使用Task,如果版本不支持Task,则考虑ThreadPool

4、Parallel

Parallel循环开启多线程,并行任务,对于多线程开启任务,开启的顺序都是不确定的

Parallel.Invoke()

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Action[] action = new Action[] {
    ()=>Console.WriteLine($"线程:{Thread.CurrentThread.ManagedThreadId}"),
    ()=>Console.WriteLine($"线程:{Thread.CurrentThread.ManagedThreadId}"),
    ()=>Console.WriteLine($"线程:{Thread.CurrentThread.ManagedThreadId}"),
    ()=>Console.WriteLine($"线程:{Thread.CurrentThread.ManagedThreadId}"),
    ()=>Console.WriteLine($"线程:{Thread.CurrentThread.ManagedThreadId}"),
    ()=>Console.WriteLine($"线程:{Thread.CurrentThread.ManagedThreadId}"),
    ()=>Console.WriteLine($"线程:{Thread.CurrentThread.ManagedThreadId}"),
    ()=>Console.WriteLine($"线程:{Thread.CurrentThread.ManagedThreadId}"),
    ()=>Console.WriteLine($"线程:{Thread.CurrentThread.ManagedThreadId}"),
    ()=>Console.WriteLine($"线程:{Thread.CurrentThread.ManagedThreadId}"),
};
Parallel.Invoke(action);

相当于

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Action[] action = new Action[] {
    ()=>Console.WriteLine($"线程:{Thread.CurrentThread.ManagedThreadId}"),
    ()=>Console.WriteLine($"线程:{Thread.CurrentThread.ManagedThreadId}"),
    ()=>Console.WriteLine($"线程:{Thread.CurrentThread.ManagedThreadId}"),
    ()=>Console.WriteLine($"线程:{Thread.CurrentThread.ManagedThreadId}"),
    ()=>Console.WriteLine($"线程:{Thread.CurrentThread.ManagedThreadId}"),
    ()=>Console.WriteLine($"线程:{Thread.CurrentThread.ManagedThreadId}"),
    ()=>Console.WriteLine($"线程:{Thread.CurrentThread.ManagedThreadId}"),
    ()=>Console.WriteLine($"线程:{Thread.CurrentThread.ManagedThreadId}"),
    ()=>Console.WriteLine($"线程:{Thread.CurrentThread.ManagedThreadId}"),
    ()=>Console.WriteLine($"线程:{Thread.CurrentThread.ManagedThreadId}"),
};
for (int i = 0; i < action.Length; i++)
{
    Task.Run(action[i]);
}

Invoke时也可以进行一些配置,例如配置线程池中只能最多保持一个线程

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Action[] action = new Action[] {
    ()=>Console.WriteLine($"线程:{Thread.CurrentThread.ManagedThreadId}"),
    ()=>Console.WriteLine($"线程:{Thread.CurrentThread.ManagedThreadId}"),
    ()=>Console.WriteLine($"线程:{Thread.CurrentThread.ManagedThreadId}"),
    ()=>Console.WriteLine($"线程:{Thread.CurrentThread.ManagedThreadId}"),
    ()=>Console.WriteLine($"线程:{Thread.CurrentThread.ManagedThreadId}"),
    ()=>Console.WriteLine($"线程:{Thread.CurrentThread.ManagedThreadId}"),
    ()=>Console.WriteLine($"线程:{Thread.CurrentThread.ManagedThreadId}"),
    ()=>Console.WriteLine($"线程:{Thread.CurrentThread.ManagedThreadId}"),
    ()=>Console.WriteLine($"线程:{Thread.CurrentThread.ManagedThreadId}"),
    ()=>Console.WriteLine($"线程:{Thread.CurrentThread.ManagedThreadId}"),
};
Parallel.Invoke(new ParallelOptions()
{
    MaxDegreeOfParallelism = 1
}, action);

运行结果:

Parallel.For()

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//将迭代的结果保存起来
ParallelLoopResult plr =  Parallel.For(1, 10, (i) =>
{
    Console.WriteLine($"线程:{Thread.CurrentThread.ManagedThreadId}");
});
Console.WriteLine(plr.IsCompleted);

相当于

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
for (int i = 1; i < 10; i++)
{
    Task.Run(() =>
    {
        Console.WriteLine($"线程:{Thread.CurrentThread.ManagedThreadId}");
    });
}

相对于循环Task.Run()更加简洁

Parallel.ForEach()

方法和foreach类似,不过是采用的是异步方式遍历,要想被Parallel.ForEach()必须实现IEnumerable接口

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Parallel.ForEach<String>(new List<String>() {
    "a","b","c","d","e","f","g","h","i"
}, (str) =>
{
    Console.WriteLine(str);
});

运行结果:

停止循环的方法

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//将迭代的结果保存起来
ParallelLoopResult plr =  Parallel.For(1, 10, (i,state) =>
{
    Console.WriteLine($"线程:{Thread.CurrentThread.ManagedThreadId}");
    if (i==4) {
        //结束
        state.Break();
    }
});
Console.WriteLine(plr.IsCompleted);

5、Async、Await

async和await关键字用来实现异步编程,async用来修饰方法,await用来调用方法,await关键字必须出现在有async的方法中,await调用的方法可以不用async关键字修饰,但是返回值类型必须为Task<T>类型,下面来说一下用法:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
static void Main(string[] args)
{
    Demo1();
    Console.ReadKey();
}
static async void  Demo1()
{
    await Demo2();
}
static async Task<int> Demo2()
{
    return 1;
}

await开启异步和Task开启异步还是有区别的

例如下面两个例子

我们先用Task开启异步编程

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
static void Main(string[] args)
{
    Console.WriteLine("主线程开始");
    TaskDemo1();
    Console.WriteLine("主线程结束");
    Console.ReadKey();
}

static void TaskDemo1() {
    Console.WriteLine("异步开始");
    Task.Run<int>(() =>
    {
        return TaskDemo2();
    });
    Console.WriteLine("异步结束");
}

static int TaskDemo2()
{
    Console.WriteLine("子线程开始");
    Thread.Sleep(1000);
    Console.WriteLine("子线程结束");
    return 1;
}

我们这是可以大胆的猜测一下显示的顺寻

大致应该是:主线程开始==》异步开始==》(子线程开始|异步结束)=》(子线程开始|主线程结束)==》(子线程开始)=》子线程结束

运行结果:

果然和我们猜想的差不多,大致顺序没有变,接下来我们用async和await关键字开启异步

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
static void Main(string[] args)
{
    Console.WriteLine("主线程开始");
    AsyncDemo1();
    Console.WriteLine("主线程结束");
    Console.ReadKey();
}
static async void AsyncDemo1()
{
    Console.WriteLine("异步开始");
    await AsyncDemo2();
    Console.WriteLine("异步结束");
}

static async Task<int> AsyncDemo2()
{
    Console.WriteLine("子线程开始");
    //当前子线程暂停1s
    await Task.Delay(1000);
    Console.WriteLine("子线程结束");
    return 0;
}

按理说顺序也会是:主线程开始==》异步开始==》(子线程开始|异步结束)=》(子线程开始|主线程结束)==》(子线程开始)=》子线程结束

但事实是:

Task和async&await关键字的区别就此处

首先说一下梳理一下Task的执行过程(画图画的很粗糙,重点是流程)

然后我们再来看一下async和await的执行过程

现在问题已经很清晰了,就是当主线程执行到await AsyncDemo2()时,会像是碰到了return语句一样,退出当前方法(AsyncDemo1),将当前方法(AsyncDemo1)的后续执行语句交给子线程来执行,子线程会在执行完AsyncDemo2方法之后,返回过来执行AsyncDemo1方法。

这一点就是await与Task异步编程的不同点

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-08-12,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 DotNet技术平台 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
python科学计算学习二:matplo
    博客地址:http://blog.csdn.net/ikerpeng/article/details/20523679
py3study
2020/01/06
7940
Python数据可视化-第6章-坐标轴的定制
matplotlib支持向画布的任意位置添加自定义大小的绘图区域,同时显示坐标轴。通过pyplot模块的axes()函数创建一个Axes类的对象,并将Axes类的对象添加到当前画布中。
用户2225445
2025/04/04
1770
Python数据可视化-第6章-坐标轴的定制
【MATLAB】进阶绘图 ( 进阶绘图展示 | 对数图 | semilogx 半对刻度数图 | semilogy 半对数刻度图 | loglog 双对数刻度图 )
logspace 函数参考文档 : https://ww2.mathworks.cn/help/matlab/ref/logspace.html
韩曙亮
2023/03/29
2.5K0
【MATLAB】进阶绘图 ( 进阶绘图展示 | 对数图 | semilogx 半对刻度数图 | semilogy 半对数刻度图 | loglog 双对数刻度图 )
设置坐标轴刻度的位置和样式
在matplotlib中,通过子模块ticker可以对坐标轴刻度的位置和样式进行设置。刻度线分为major和minor ticks, 通过以下4个函数可以对其位置和样式进行设置
生信修炼手册
2020/09/23
3.2K0
设置坐标轴刻度的位置和样式
[Python]Matplotlib绘图基础
1.简介 Matplotlib 是一个 Python 的 2D绘图库,它以各种硬拷贝格式和跨平台的交互式环境生成出版质量级别的图形。 ---- 2.绘图基础 2.1 图表基本元素 图例和标题 x轴和y轴、刻度、刻度标签 绘图区域及边框 网格线 2.2 图表基本属性 多重绘图属性: 是否在同一个图上绘制多个系列的线 多重子图属性: 是否生成多个子图,并在每个子图上绘制多个系列的线 ---- 3.绘图方式 3.1 Pyplot API[1] 3.1.1 属性设置函数 绘制图边框: box 为图表添加图例: fi
昊楠Hacking
2018/03/08
3K0
[Python]Matplotlib绘图基础
matplotlib的基本用法——调整坐标轴
本文主要是关于matplotlib的一些基本用法。 Demo import matplotlib.pyplot as plt import numpy as np # 绘制普通图像 x = np.linspace(-1, 1, 50) y1 = 2 * x + 1 y2 = x**2 plt.figure() plt.plot(x, y1) plt.plot(x, y2, color = 'red', linewidth = 1.0, linestyle = '--') # 设置坐标轴的取值范围 p
Tyan
2019/05/25
1.5K0
绘制双坐标轴图
双坐标轴图作为常用的可视化方式之一,可以在同一张图中同时展示两个不同范围的数据,示例如下
生信修炼手册
2020/08/28
1.6K0
python实现之初等函数一
初等函数是由基本初等函数经过有限次的四则运算和复合运算所得到的函数。基本初等函数和初等函数在其定义区间内均为连续函数。高等数学将基本初等函数归为五类:幂函数、指数函数、对数函数、三角函数、反三角函数。
python与大数据分析
2022/03/11
3370
python实现之初等函数一
python实现之导数
导数(Derivative),也叫导函数值。又名微商,是微积分中的重要基础概念。当函数y=f(x)的自变量x在一点x0上产生一个增量Δx时,函数输出值的增量Δy与自变量增量Δx的比值在Δx趋于0时的极限a如果存在,a即为在x0处的导数,记作f'(x0)或df(x0)/dx。
python与大数据分析
2022/03/11
5460
python实现之导数
Matlab系列之二维图形(下)
交互式绘图的意思就是可以使用鼠标对图形进行操作,具体的实现看待会的演示,首先了解几个会用到的函数:ginput、gtext和zoom,其中ginput只能用于二维图形绘制,另外两个还适用于三维图形绘制。
狂人V
2021/04/29
1.5K0
Matlab系列之二维图形(下)
python 中numpy基本方法总结可以类推tensorflow
一、数组方法 创建数组:arange()创建一维数组;array()创建一维或多维数组,其参数是类似于数组的对象,如列表等 反过来转换则可以使用numpy.ndarray.tolist()函数,如a.tolist() 创建数组:np.zeros((2,3)),或者np.ones((2,3)),参数是一个元组分别表示行数和列数 对应元素相乘,a * b,得到一个新的矩阵,形状要一致;但是允许a是向量而b是矩阵,a的列数必须等于b的列数,a与每个行向量对应元素相乘得到行向量。 + - / 与 * 的运
学到老
2018/03/16
2.1K0
Python数据可视化-第5章-子图的绘制及坐标轴共享
使用pyplot模块的subplot()函数可以在规划好的某个区域中绘制单个子图。
用户2225445
2025/04/01
2630
Python数据可视化-第5章-子图的绘制及坐标轴共享
【6】python生成数据曲线平滑处理——(Savitzky-Golay 滤波器、convolve滑动平均滤波)方法介绍,推荐玩强化学习的小伙伴收藏
 由于高频某些点的波动导致高频曲线非常难看,为了降低噪声干扰,需要对曲线做平滑处理,让曲线过渡更平滑,可以看出经过平滑处理后更明显去除噪声且更加美观。
汀丶人工智能
2022/12/21
3.3K0
【6】python生成数据曲线平滑处理——(Savitzky-Golay 滤波器、convolve滑动平均滤波)方法介绍,推荐玩强化学习的小伙伴收藏
python实现之初等函数二——反函数
一般来说,设函数y=f(x)(x∈A)的值域是C,若找得到一个函数g(y)在每一处g(y)都等于x,这样的函数x= g(y)(y∈C)叫做函数y=f(x)(x∈A)的反函数,记作x=f-1(y) 。反函数x=f -1(y)的定义域、值域分别是函数y=f(x)的值域、定义域。最具有代表性的反函数就是对数函数与指数函数。
python与大数据分析
2022/03/11
5980
python实现之初等函数二——反函数
用不同的坐标系统对图形元素进行定位
当我们在绘制图形元素时,需要通过x轴和y轴的坐标来指定具体的位置,这里的x轴和y轴就是我们最常用的坐标系统。其实在matplotlib中,还有很多其他的坐标系统, 常用的坐标系统主要包括以下3类
生信修炼手册
2020/09/14
9670
【matplotlib】4-完善统计图形
在绘图区域中可能会出现多个图形,而这些图形如果不加以说明,观察者则很难识别出这些图形的主要内容。因此,我们需要给这些图形添加标签说明,用以标记每个图形所代表的的内容。方便观察者辨识,这个标签说明就是图例。 同样,如果观察者想要清楚地了解绘图区域中的内容。就需要给绘图区域添加文本内容用以说明绘图区域的主要内容,标题就可以让观察者清楚地知道绘图区域的核心信息和图标内容。
程序员小涛
2023/05/09
2.7K0
【matplotlib】4-完善统计图形
Matplotlib for C++不完全手册
matplotlib-cpp是Matplotlib(MPL)为C++提供的一个用于python的matplotlib绘图库的C++包装器。它的构建类似于Matlab和matplotlib使用的绘图API。
艰默
2024/01/11
1.1K0
Matplotlib for C++不完全手册
对图标的坐标轴进行调整
坐标轴包含了标题,刻度线,刻度标签等多个元素,通过对各个元素进行个性化调整,可以使得图片更加美观。在matplotlib中,包含了一系列函数,可以对各种元素进行调整
生信修炼手册
2020/08/28
1.7K0
NumPy Beginner's Guide 2e 带注释源码 九、使用 Matplotlib 绘图
# 来源:NumPy Biginner's Guide 2e ch9 绘制多项式函数 import numpy as np import matplotlib.pyplot as plt # 创建函数 func = x ** 3 + 2 * x ** 2 + 3 * x + 4 # poly1d 根据系数数组创建函数,高项系数在前 func = np.poly1d(np.array([1, 2, 3, 4]).astype(float)) # x 值是 -10 ~ 10 取 30 个点 x = n
ApacheCN_飞龙
2019/02/15
7190
NumPy Beginner's Guide 2e 带注释源码 九、使用 Matplotlib 绘图
Python | numpy matplotlib scipy练习笔记
return y - (t[0] * x**2 + t[1] * x + t[2])
用户7886150
2021/01/02
6700
相关推荐
python科学计算学习二:matplo
更多 >
LV.1
这个人很懒,什么都没有留下~
目录
  • 多线程的操作在程序中也是比较常见的,比如开启一个线程执行一些比较耗时的操作(IO操作),而主线程继续执行当前操作,不会造成主线程阻塞。线程又分为前台线程和后台线程,区别是:整个程序必须要运行完前台线程才会退出,而后台线程会在程序退出的时候结束掉。Thread默认创建的是前台线程,而ThreadPool和Task默认创建的是后台线程,Thread可以通过设置 IsBackground 属性将线程设置为后台线程。
  • 下面来说一下几种开启多线程的方法:
  • 1、Thread
    • 1.1 开启一个线程,执行一个不带参数的方法
    • 1.2开启一个线程,执行带参数的方法
  • 2、ThreadPool
    • 使用ThreadPool开启一个线程
    • 二者之间还有一个区别,就是ThreadPool可以操控线程的状态,比如等待线程完成,或者终止超时子线程操作
    • ThreadPool的其他操作感兴趣的可以自己搜索学一下,因为终止线程什么操作都是比较麻烦的,关于ThreadPool就不再多说了
  • 3、Task
    • Task和ThreadPool是一样的,都是从线程池中取空闲的线程
    • 使用Task开启一个线程
  • 对于Thread、ThreadPool和Task,如果要用多线程的话,优先使用Task,如果版本不支持Task,则考虑ThreadPool
  • 4、Parallel
    • Parallel.Invoke()
    • Parallel.For()
    • Parallel.ForEach()
  • 5、Async、Await
    • Task和async&await关键字的区别就此处
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档