如何在async异步编程中实现类似协程WaitUntil的条件等待功能?
实现方案
完全可以实现你需要的WaitUntil异步等待逻辑,只需要自行封装对应异步方法即可,以下是不同场景的实现参考:
1. 基础通用实现
这个版本不需要依赖第三方库,适用于所有C# async/await场景:
public static class TaskExtensions { public static async Task WaitUntil(Func<bool> condition, int checkIntervalMs = 50, CancellationToken cancellationToken = default) { // 每次检测间隔默认50ms,可根据精度需求自行调整 while (!condition()) { // 检测取消信号,避免死等待 cancellationToken.ThrowIfCancellationRequested(); // 等待指定间隔后再下一次检测,不阻塞当前线程 await Task.Delay(checkIntervalMs, cancellationToken); } } }
调用方式和你期望的完全一致:
// 基础调用,等待直到条件满足 await TaskExtensions.WaitUntil(() => conditionIsMet); // 自定义检测间隔+传入取消令牌的进阶调用 await TaskExtensions.WaitUntil(() => conditionIsMet, 10, cancellationToken);
如果你想实现和你写的完全一样的Task.WaitUntil调用形式,可以给Task类加静态扩展(C# 7.0及以上版本支持静态扩展方法),或者直接自定义partial的Task类添加对应静态方法。
2. 高响应度低延迟实现
如果你对等待的响应速度要求很高,不想因为固定检测间隔产生延迟,可以用SpinWait替代Task.Delay,适合条件会很快满足的场景:
public static async Task WaitUntil(Func<bool> condition, CancellationToken cancellationToken = default) { var spinWait = new SpinWait(); while (!condition()) { cancellationToken.ThrowIfCancellationRequested(); // 先自旋几次,再让出线程时间片 spinWait.SpinOnce(); // 自旋次数到阈值后用Task.Yield避免占用过多CPU if (spinWait.NextSpinWillYield) { await Task.Yield(); } } }
注意事项
- 建议传入
CancellationToken,避免条件永远不满足时出现死等待问题 - 如果条件是UI相关的状态检测,要保证条件委托的执行线程是UI线程,避免出现线程安全问题
- 固定间隔版本CPU占用更低,适合等待时间较长的场景;自旋版本响应更快,适合等待时间很短的高实时性场景
内容的提问来源于stack exchange,提问作者Noob001
相关产品推荐
相关产品推荐

