Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >async 与 Thread 的错误结合

async 与 Thread 的错误结合

作者头像
newbe36524
发布于 2023-08-23 08:20:20
发布于 2023-08-23 08:20:20
20100
代码可运行
举报
运行总次数:0
代码可运行

在 TAP 出现之前,我们可以通过 Thread 来完成一些线程操作,从而实现多线程和异步操作。在 TAP 出现之后,有时候为了更高精度的控制线程,我们还是会使用到 Thread 。文本讲介绍一种错误的使用方式,作为读者的一个参考。

和 TaskCreateOptions.LongRunning 类似

不应该尝试使用 Thread 执行类似的异步操作。因为这浪费了开启线程的花销。

有的时候,你可能会这么写:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var thread = new Thread(async () =>
{
    while (true)
    {
        // do something
        await Task.Delay(1000);
    }
}){
    IsBackground = true
};
thread.Start();

但其实,这是个错误的写法。

IDE 提示

和 TaskCreateOptions.LongRunning 略有不同,采用这种写法,IDE 会给出一个提示,表明希望取消 async 关键字。因为实际上

  1. Thread 的所有重载中并没有支持 Task 相关的重载。
  2. async void 除了在 event handler 中使用,其他地方都是不推荐的。

所以这种做法实际上并不推荐。

而 TaskFactory.StartNew() 的重载中,由于存在一个 Func<T> 的重载,所以导致虽然这种这种使用方式错误,却被 IDE 所接受。

所以这里其实就可以总结一个简单的规则:当考察一组 API 是否原生支持 TAP 操作的时候,应该查看这组 API 中是否存在 Task 相关的重载。如果没有,那么说明原生并不能良好支持,如果使用则可能会出现意外的情况

同样的,当我们自己在设计 API 的时候也应该参考该原则,对于自己希望支持 TAP 的 API,应该提供 Task 相关的重载。

昙花线程

在 thread async void 其实上只是一个很小的问题。这个错误的关键还是造成了一个昙花线程。

我们通过以下代码来验证:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var thread = new Thread(async () =>
{
    while (true)
    {
        // do something
        await Task.Delay(1000);
    }
}){
    IsBackground = true
};
thread.Start();

Thread.Sleep(3000);

Console.WriteLine("thread is alive: " + thread.IsAlive);
// thread is alive: False

这里我们可以看到,thread.IsAlive 的值为 False。这是因为,我们在 thread 中使用了 await 关键字,在 await 之后的代码,实际上是在另一个 ThreadPool 中的线程中执行的。而我们的 thread 本身在 await 之后就已经结束了。于是我们就得到了一个昙花一现的线程。

而这种昙花线程无疑就是一种浪费。

如何观测线程的生命周期

其实大体的内容我们已经讲完了。但为了凑一下篇幅,我们着重演示一下如何使用 Rider 来观测线程的生命周期。

首先我们在 Rider 中创建一个单元测试项目,然后在其中创建一个单元测试:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
[Test]
public void Test1()
{
    var t1 = new Thread(async () =>
    {
        while (true)
        {
            // do something
            await Task.Delay(1000);
        }
    })
    {
        IsBackground = true,
        Name = "t1"
    };
    t1.Start();

    var t2 = new Thread(() =>
    {
        while (true)
        {
            // do something
            Thread.Sleep(1000);
        }
    })
    {
        IsBackground = true,
        Name = "t2"
    };
    t2.Start();

    Thread.Sleep(3000);
}

然后我们在 Rider 中按照下图选择 Profile 选项:

然后选择 Profile Unit Tests 选项:

稍等片刻之后,我们就可以双击下图中的报告,来查看线程的生命周期:

在查看界面中,我们可以通过线程下来框来查看线程运行所花费的时间:

如果上图,我们可以很直接的看到,t1 线程的生命周期可以说是瞬间就结束了,因为第一次 await 之后,线程就结束了。

总结

在本文中,我们演示了一种错误的使用方式,以及如何使用 Rider 来观测线程的生命周期。

参考

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2023-03-12,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

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

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

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
实现常驻任务除了避免昙花线程,还需要避免重返线程池
前面我们使用简单的例子演示了 Task 和 Thread 的两种制造昙花线程的方式。那么除了避免昙花线程,在实现常驻任务的时候,还需要避免重返线程池。本文将介绍如何避免重返线程池。
newbe36524
2023/03/20
3110
实现常驻任务除了避免昙花线程,还需要避免重返线程池
我没能实现始终在一个线程上运行 task
前文我们总结了在使用常驻任务实现常驻线程时,应该注意的事项。但是我们最终没有提到如何在处理对于带有异步代码的办法。本篇将接受笔者对于该内容的总结。
newbe36524
2023/04/03
5530
我没能实现始终在一个线程上运行 task
理解Task和和async await
本文将详解C#类当中的Task,以及异步函数async await和Task的关系
ryzenWzd
2020/11/12
2.4K0
理解Task和和async await
这样在 C# 使用 LongRunningTask 是错的
Task.Factory.StartNew 有一个重载,是支持 TaskCreationOptions.LongRunning 参数来指定 Task 的特征的。但是可能在没有注意的情况下,你就使用了错误的用法。那么本文我们来简单阐述一下这个参数的作用,和使用的注意要点。
newbe36524
2023/08/23
5120
这样在 C# 使用 LongRunnigTask 是错的
Task.Factory.StartNew 有一个重载,是支持 TaskCreationOptions.LongRunning 参数来指定 Task 的特征的。但是可能在没有注意的情况下,你就使用了错误的用法。那么本文我们来简单阐述一下这个参数的作用,和使用的注意要点。
newbe36524
2023/03/06
8930
这样在 C# 使用 LongRunnigTask 是错的
.NET Core多线程 (1) Thread与Task
.NET Core中加入了本地队列,加入了本地队列,降低了锁竞争,并提高了线程的利用率。
Edison Zhou
2023/08/09
6110
.NET Core多线程 (1) Thread与Task
Thread类的方法使用
start()方法可以开启一个线程,线程创建好后是不会开始执行的,要使用start来开启。
用户11162265
2024/11/20
1280
Thread类的方法使用
Thread、ThreadPool、Task、Parallel、Async和Await基本用法、区别以及弊端
ThreadPool是Thread的一个升级版,ThreadPool是从线程池中获取线程,如果线程池中又空闲的元素,则直接调用,如果没有才会创建,而Thread则是会一直创建新的线程,要知道开启一个线程就算什么事都不做也会消耗大约1m的内存,是非常浪费性能的,接下来我们写一个例子来看一下二者的区别:
AI.NET 极客圈
2019/08/14
1.9K0
C#:异步编程和线程的使用(.NET 4.5 )
异步编程和线程处理是并发或并行编程非常重要的功能特征。为了实现异步编程,可使用线程也可以不用。将异步与线程同时讲,将有助于我们更好的理解它们的特征。 本文中涉及关键知识点 1. 异步编程 2. 线程的使用 3. 基于任务的异步模式 4. 并行编程 5. 总结 异步编程 什么是异步操作?异步操作是指某些操作能够独立运行,不依赖主流程或主其他处理流程。通常情况下,C#程序从Main方法开始,当Main方法返回时结束。所有的操作都是按顺序执行的。执行操作是有序列的,一个操作必须等到其前面的操作完成才能够
葡萄城控件
2018/01/10
1.9K0
C#:异步编程和线程的使用(.NET 4.5 )
Asp.Net Core 轻松学-多线程之Task快速上手
    Task是从 .NET Framework 4 开始引入的一项基于队列的异步任务(TAP)模式,从 .NET Framework 4.5 开始,任何使用 async/await 进行修饰的方法,都会被认为是一个异步方法;实际上,这些异步方法都是基于队列的线程任务,从你开始使用 Task 去运行一段代码的时候,实际上就相当于开启了一个线程,默认情况下,这个线程数由线程池 ThreadPool 进行管理的。
梁规晓
2019/04/11
1.6K0
Asp.Net Core 轻松学-多线程之Task快速上手
.NET系列走进Task:Task的回调执行与await
上一篇我们讲了对 Task 的基本定义:Task 代表一个任务,其具体类型可能是多种多样的,且有时候对我们来说完全是个黑盒。这个任务可以有结果,可以没有结果,我们能知道这个任务什么时候执行完成,并进行相应的后续处理。
郑子铭
2022/03/22
2.7K0
.NET系列走进Task:Task的回调执行与await
C#/.NET 中 Thread.Sleep(0), Task.Delay(0), Thread.Yield(), Task.Yield() 不同的执行效果和用法建议
在 C#/.NET 中,有 Thread.Sleep(0), Task.Delay(0), Thread.Yield(), Task.Yield() 中,有几种不同的让当前线程释放执行权的方法。他们的作用都是放弃当前线程当前的执行权,让其他线程得以调度。但是他们又不太一样。
walterlv
2023/10/22
1.6K0
C#/.NET 中 Thread.Sleep(0), Task.Delay(0), Thread.Yield(), Task.Yield() 不同的执行效果和用法建议
C# 中的线程与任务 — 有什么区别?
在C#编程中,类(class)是一种让我们可以同时执行任务的方式,允许我们在程序的其他部分继续运行时执行代码。尽管现代C#开发人员通常使用Task来管理并发性,但Thread类提供了更多的线程行为控制,这使得它在需要进行低级别线程操作时非常有用。
郑子铭
2025/01/07
2490
C# 中的线程与任务 — 有什么区别?
[C#.NET 拾遗补漏]16:几个常见的TAP异步操作
在本系列上一篇文章 [15:异步编程基础] 中,我们讲到,现代应用程序广泛使用的是基于任务的异步编程模式(TAP),历史的 EAP 和 AMP 模式已经过时不推荐使用。今天继续总结一下 TAP 的异步操作,比如取消任务、报告进度、Task.Yield()、ConfigureAwait() 和并行操作等。
JusterZhu
2022/12/07
8460
C#:异步编程中的 async 和 await
async 和 await 在 C# 5.0 就已经引入了,用来处理异步编程,但之前用的相对较少,现在在 dotNet Core 时代,已经使用的非常普遍,很多的开源组件中提供了大量的后缀为 Async (异步)的方法。本文就简单讲讲 async 和 await。
oec2003
2020/11/19
2.7K0
C#:异步编程中的 async 和 await
C# 多线程技术
在讲线程之前,先区分一下程序,进程,线程三者的区别,大体上说,一个程序可以分为多个进程,一个进程至少由一个线程去执行,它们是层层包含的关系。我们写的程序,就是一个进程,多个进程,以完成一个用户服务,或者完成一个大的界面展现,就组成一个程序,但在CPU层面,只有线程的概念,线程是最小的执行单位,Windows中采用CPU轮换制度,CPU给每个要执行的线程分配操作时间,轮流执行,但因为CPU的主频实在是太高,我们感受不到每个程序轮空期卡顿。
宿春磊Charles
2022/03/29
4420
C# 多线程技术
C# 异步编程与多线程简析:Thread、ThreadPool、Task
C# 提供了多种机制来支持异步编程和多线程,其中Thread、ThreadPool 和Task 是最为常用的三种。
软件架构师Michael
2024/12/24
3150
.net 温故知新:【5】异步编程 async await
上面是关于异步编程的解释,我们日常编程过程或多或少的会使用到异步编程,为什么要试用异步编程?因为用程序处理过程中使用文件和网络 I/O,比如处理文件的读取写入磁盘,网络请求接口API,默认情况下 I/O API 一般会阻塞。 这样的结果是导致我们的用户界面卡住体验差,有些服务器的硬件利用率低,服务处理能力请求响应慢等问题。基于任务的异步 API 和语言级异步编程模型改变了这种模型,只需了解几个新概念就可默认进行异步执行。
SpringSun
2021/08/31
8630
.net 温故知新:【5】异步编程 async await
C#中关于Task.Yeild()的探究
在与同事讨论async/await内部实现的时候,突然想到Task.Yeild()这个函数,为什么呢,了解一点C#async/await内部机制的都知道,在await一个异步任务(函数)的时候,它会先判断该Task是否已经完成,如果已经完成,则继续执行下去,不会返回到调用方,原因是尽量避免线程切换,因为await后面部分的代码很可能是另一个不同的线程执行,而Task.Yeild()则可以强制回到调用方,或者说主动让出执行权,给其他Task执行的机会,可以把Task理解为协程,Task.Yeild()和Thr
Jlion
2022/04/07
3890
C#中关于Task.Yeild()的探究
【JavaEE】线程创建和终止,Thread类方法,变量捕获(7000字长文)
阿华代码,不是逆风,就是我疯,你们的点赞收藏是我前进最大的动力!!7000字长文,希望本文内容能够帮助到你!
三三是该溜子
2024/12/30
940
【JavaEE】线程创建和终止,Thread类方法,变量捕获(7000字长文)
推荐阅读
相关推荐
实现常驻任务除了避免昙花线程,还需要避免重返线程池
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档