登录
/
注册
首页
论坛
其它
首页
科技
业界
安全
程序
广播
Follow
关于
博客
发1篇日志+1圆
记录
发1条记录+2圆币
发帖说明
登录
/
注册
账号
自动登录
找回密码
密码
登录
立即注册
搜索
搜索
关闭
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
感谢阅读,如果觉得本文有用,不妨点击推荐
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
回复
使用道具
举报
提升卡
置顶卡
沉默卡
喧嚣卡
变色卡
千斤顶
照妖镜
高级模式
B
Color
Image
Link
Quote
Code
Smilies
您需要登录后才可以回帖
登录
|
立即注册
回复
本版积分规则
回帖并转播
回帖后跳转到最后一页
签约作者
程序园优秀签约作者
发帖
啖曼烟
2025-6-2 23:36:55
关注
0
粉丝关注
16
主题发布
板块介绍填写区域,请于后台编辑
财富榜{圆}
敖可
9984
黎瑞芝
9990
杭环
9988
4
猷咎
9988
5
凶契帽
9988
6
氛疵
9988
7
恐肩
9986
8
虽裘侪
9986
9
接快背
9986
10
里豳朝
9986
查看更多