登录
/
注册
首页
论坛
其它
首页
科技
业界
安全
程序
广播
Follow
关于
签到
每天签到奖励2-10圆
导读
排行榜
TG频道
发帖说明
登录
/
注册
账号
自动登录
找回密码
密码
登录
立即注册
搜索
搜索
关闭
CSDN热搜
程序园
精品问答
技术交流
资源下载
本版
帖子
用户
软件
问答
教程
代码
写记录
VIP申请
VIP网盘
网盘
联系我们
发帖说明
每日签到
道具
勋章
任务
淘帖
动态
分享
留言板
导读
设置
我的收藏
退出
腾讯QQ
微信登录
返回列表
首页
›
业界区
›
业界
›
好哥哥因为没有搞清楚同步完成和异步完成导致代码死循环 ...
好哥哥因为没有搞清楚同步完成和异步完成导致代码死循环了这档事
[ 复制链接 ]
啖曼烟
2025-6-2 23:36:55
有个好哥哥说他遇到循环代码死循环的问题,所以仔细的研究了一下,于是发现了关于同步完成和异步完成的区别。
什么是死循环
比如,长这个样子
[Test]
public void EndlessLoop()
{
Yanjia_foRever_niCE();
Console.WriteLine("nice end"); // it will never reach here
return;
void Yanjia_foRever_niCE()
{
do
{
Thread.Sleep(100); // some code running synchronously
Console.WriteLine("严架 nice");
} while (GetBool());
return;
bool GetBool()
{
return true;
}
}
}
复制代码
❌ 这段代码一但开始运行,将不会结束,也不会打印 nice end。 因为 Yanjia_foRever_niCE 这个方法是一个死循环,永远不会结束。
那如果 GetBool() 返回 Task 呢
比如,长这个样子
[Test]
public void EndlessLoopWithSyncCompletion()
{
Yanjia_foRever_niCE();
Console.WriteLine("nice end"); // it will never reach here
return;
async Task Yanjia_foRever_niCE()
{
do
{
Thread.Sleep(100); // some code running synchronously
Console.WriteLine("严架 nice");
} while (await GetBoolAsync());
return;
Task<bool> GetBoolAsync()
{
return Task.FromResult(true);
}
}
}
复制代码
❌ 这个代码,看起来用了 Task 但是依然是一个死循环。因为 GetBoolAsync 返回的是一个 Task,但它是一个同步完成的 Task。所有的操作都会在当前线程完成,不会切换到其他线程。
聪明的宝宝已经想到了可以加上 async await
比如,长这个样子
[Test]
public void EndlessLoopWithSyncCompletion2()
{
Yanjia_foRever_niCE();
Console.WriteLine("nice end"); // it will never reach here
return;
async Task Yanjia_foRever_niCE()
{
do
{
Thread.Sleep(100); // some code running synchronously
Console.WriteLine("严架 nice");
} while (await GetBoolAsync());
return;
async Task<bool> GetBoolAsync()
{
return true;
}
}
}
复制代码
❌ 但实际上,没有任何卵用,因为 GetBoolAsync 本质依然是一个同步完成的 Task。所有的操作都会在当前线程完成,不会切换到其他线程。 并且实际上会触发编译器警告,乖宝宝可不要这样写哟。
但是我用 await Task.FromResult(true) 很开心
比如,长这个样子
[Test]
public void EndlessLoopWithSyncCompletion3()
{
Yanjia_foRever_niCE();
Console.WriteLine("nice end"); // it will never reach here
return;
async Task Yanjia_foRever_niCE()
{
do
{
Thread.Sleep(100); // some code running synchronously
Console.WriteLine("严架 nice");
} while (await GetBoolAsync());
return;
async Task<bool> GetBoolAsync()
{
return await Task.FromResult(true);
}
}
}
复制代码
❌ 确实曾经有好哥哥这么写过代码,但是实际上 await Task.FromResult(true) 依然是一个同步完成的 Task。 async 和 await 是不会改变 Task 的完成方式的。 它是同步完成,就一直都是同步完成。当然这样写还有一个好处就是可以略微增加代码量 XD。
真的异步才行
比如,长这个样子
[Test]
public void EndlessLoopWithAsyncCompletion()
{
Yanjia_foRever_niCE();
Console.WriteLine("nice end"); // it will reach here
return;
async Task Yanjia_foRever_niCE()
{
do
{
Thread.Sleep(100); // some code running synchronously
Console.WriteLine("严架 nice");
} while (await GetBoolAsync());
return;
async Task<bool> GetBoolAsync()
{
await Task.Delay(100);
return true;
}
}
}
复制代码
✅ 这段代码就可以正常结束了,因为 GetBoolAsync 返回的是一个异步完成的 Task。其中的 await Task.Delay(100) 会真的触发异步完成。从而导致 Yanjia_foRever_niCE 由于没有 await 而直接推进到 nice end 这行代码。
有人好奇 ValueTask 会怎么样
实际上上面所有的 Task 换成 ValueTask 都是一样的。比如
ValueTask<bool> GetBoolAsync() // ❌
{
return ValueTask.FromResult(true);
}
async ValueTask<bool> GetBoolAsync() // ❌
{
return true;
}
async ValueTask<bool> GetBoolAsync() // ❌
{
return await ValueTask.FromResult(true);
}
async ValueTask<bool> GetBoolAsync() // ✅
{
await Task.Delay(100);
return true;
}
复制代码
这些代码产生的效果和上面所有的 Task 代码是一样的。
也就是 ValueTask 也不会影响这段代码是同步完成还是异步完成。
其实一开始是因为一个 Timer
.NET6 中的 PeriodicTimer 是一个定时器,它的 WaitForNextTickAsync 方法是一个返回 ValueTask 的方法。
它有一个很合理但是可能有时候注意不到的地方,就是计时是从创建实例开始的,而不是从调用 WaitForNextTickAsync 开始的。
比如下面这段代码:
[Test]
public void EndlessLoopWithSyncCompletion()
{
var timer = new PeriodicTimer(TimeSpan.FromSeconds(0.01));
Yanjia_foRever_niCE();
Console.WriteLine("nice end"); // it will never reach here
return;
async Task Yanjia_foRever_niCE()
{
do
{
Thread.Sleep(TimeSpan.FromSeconds(0.5)); // some code running synchronously
Console.WriteLine("严架 nice");
} while (await GetBoolAsync());
return;
ValueTask<bool> GetBoolAsync()
{
return timer.WaitForNextTickAsync();
}
}
}
复制代码
这段代码中, timer 的创建时间是 0.01 秒,而 Thread.Sleep 的时间是 0.5 秒。这也就意味者,在每次进入 GetBoolAsync 方法时, timer 早就已经到了下一个 tick 的时间了。
所以这个时候 timer.WaitForNextTickAsync() 返回的就是一个同步完成的 ValueTask。
因为它是一个同步完成的 ValueTask,所以 await 也不会切换到其他线程。从而就导致了死循环。
而,如果将 timer 的创建时间改成 1 秒,那么当 Thread.Sleep 结束时, timer 的 tick 还没有到达,所以 timer.WaitForNextTickAsync() 返回的就是一个异步完成的 ValueTask。
这时候 await GetBoolAsync() 就会切换到调度器上,从而导致 Yanjia_foRever_niCE 结束,打印 nice end。
这就是一开始好哥哥遇到的问题。
总结
代码是同步完成还是异步完成,和返回值是 Task 还是 ValueTask 没有关系,和有没有 async/await 也没有关系。
它只与实现的代码究竟有没有真异步操作有关。
测试代码在:GitHub
感谢阅读,如果觉得本文有用,不妨点击推荐
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
完成
导致
这档
循环
代码
相关帖子
循环数组,一个可以释放无锁队列的力量
代码检查过程中为什么需要涉及到编译呢?
三类代码协同模式,你要如何选?
软件开发中,如何为你的代码构建三层防护体系
一键 orchestrate 多 AI 模型,代码开发协作新革命!
如何有效提升代码覆盖率:从单元测试到集成测试的实践指南
飞算科技,让代码飞:欢迎体验 JavaAI 开发助手,参加炫技赛
代码管理平台全景解析:从新手到企业级开发的最佳实践
Roslyn 技术解析:如何利用它做代码规范检查与运行时代码生成?
DeepResearch代码浅析
vip免费申请,1年只需15美金$
回复
使用道具
举报
提升卡
置顶卡
沉默卡
喧嚣卡
变色卡
千斤顶
照妖镜
相关推荐
安全
循环数组,一个可以释放无锁队列的力量
0
469
能拘
2025-10-10
安全
代码检查过程中为什么需要涉及到编译呢?
1
704
印萍
2025-10-10
安全
三类代码协同模式,你要如何选?
0
333
跟尴
2025-10-10
安全
软件开发中,如何为你的代码构建三层防护体系
0
747
汤流婉
2025-10-11
业界
一键 orchestrate 多 AI 模型,代码开发协作新革命!
0
731
丘娅楠
2025-10-12
业界
如何有效提升代码覆盖率:从单元测试到集成测试的实践指南
1
668
倘伟
2025-10-12
业界
飞算科技,让代码飞:欢迎体验 JavaAI 开发助手,参加炫技赛
0
865
姜删懔
2025-10-13
业界
代码管理平台全景解析:从新手到企业级开发的最佳实践
1
245
博咱
2025-10-13
业界
Roslyn 技术解析:如何利用它做代码规范检查与运行时代码生成?
0
1009
辖瑁地
2025-10-15
科技
DeepResearch代码浅析
0
102
孜稞
2025-10-17
高级模式
B
Color
Image
Link
Quote
Code
Smilies
您需要登录后才可以回帖
登录
|
立即注册
回复
本版积分规则
回帖并转播
回帖后跳转到最后一页
浏览过的版块
安全
签约作者
程序园优秀签约作者
发帖
啖曼烟
2025-6-2 23:36:55
关注
0
粉丝关注
19
主题发布
板块介绍填写区域,请于后台编辑
财富榜{圆}
anyue1937
9994893
dage888
999994
3934307807
992122
4
富账慕
9983
5
邹语彤
9982
6
刎唇
9993
7
匝抽
9986
8
聚怪闩
9960
9
孙淼淼
9977
10
烯八
9954
查看更多