c# - C#中可靠的延迟执行类
问题描述
为了控制我开发的 iOS 游戏中动画之间的延迟,我在下面编写了这个“Delayer”类。但我遇到了一些罕见的随机崩溃,可能与被释放的对象有关:
0x102933344 - /var/containers/Bundle/Application/52DE96A6-70CF-4D3D-A6F0-3DDAB4F31347/DiscDrop.iOS.app/DiscDrop.iOS : mono_dump_native_crash_info
0x1029295b0 - /var/containers/Bundle/Application/52DE96A6-70CF-4D3D-A6F0-3DDAB4F31347/DiscDrop.iOS.app/DiscDrop.iOS : mono_handle_native_crash
0x10293776c - /var/containers/Bundle/Application/52DE96A6-70CF-4D3D-A6F0-3DDAB4F31347/DiscDrop.iOS.app/DiscDrop.iOS : mono_sigsegv_signal_handler_debug
0x1dd9f69fc - /usr/lib/system/libsystem_platform.dylib : <redacted>
0x1dcfd9b9c - /usr/lib/libobjc.A.dylib : <redacted>
0x1dcfd9b9c - /usr/lib/libobjc.A.dylib : <redacted>
0x1dddf7bb0 - /System/Library/Frameworks/CoreFoundation.framework/CoreFoundation : _CFAutoreleasePoolPop
0x1de871744 - /System/Library/Frameworks/Foundation.framework/Foundation : <redacted>
所以我想知道是否有人可以发现课程的问题:
public class Delayer
{
private readonly List<CancellationTokenSource> cancellationTokenSources;
public Delayer()
{
this.cancellationTokenSources = new List<CancellationTokenSource>();
}
public void DelayedCall(float delay, Action callback)
{
CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
this.cancellationTokenSources.Add(cancellationTokenSource);
_ = Delayer.CallbackAfterDelay(delay, cancellationTokenSource, delegate
{
this.cancellationTokenSources.Remove(cancellationTokenSource);
callback?.Invoke();
});
}
public void CancelAll()
{
foreach (CancellationTokenSource cancellationTokenSource in this.cancellationTokenSources)
{
cancellationTokenSource.Cancel(); // cancellationTokenSource.Dispose(); here doesnt help leak
}
this.cancellationTokenSources.Clear();
}
private static async Task CallbackAfterDelay(float delay, CancellationTokenSource cancellationTokenSource, Action callback)
{
await Task.Delay((int)(delay * 1000), cancellationTokenSource.Token);
if (cancellationTokenSource.IsCancellationRequested)
{
return;
}
try
{
callback?.Invoke();
}
catch (Exception exception)
{
System.Diagnostics.Debug.WriteLine("### Common.Delayer.DelayedCall caught exception={0}", exception.Message);
throw;
}
}
}
这是我如何使用它的示例:
this.delayer = new Delayer();
//...
this.delayer.DelayedCall(delay: 0.5f, callback: delegate
{
this.PlaySound(duration: 0.2f);
}
解决方案
该_ = Delayer.CallbackAfterDelay
任务以即发即弃的方式启动,因此如果您的实现中有任何错误,您将不会收到通知。而且我已经可以看到一个错误:await
将Task.Delay
可能导致OperationCanceledException
未捕获的结果,因此在这种情况下callback?.Invoke();
不会调用,导致关联的cancellationTokenSource
不会从列表中删除。
确保出现所有异常的最简单方法是将CallbackAfterDelay
from转换async Task
为async void
. 此更改将迫使您在编写代码时非常小心,因为此方法中的任何未捕获的异常都将未被处理(不仅仅是未观察到),并且会使进程崩溃。
推荐阅读
- c++ - C++:将字符串或字符转换为 int
- domain-driven-design - 如何在 DDD 中为实体的属性建模
- regex - RegEx - 正向后视中的可选子字符串
- r - 为什么 makeCATdb 函数不起作用?
- python - Python 使用 Sched 安排一个函数每 10 秒运行一次
- logic - 一阶逻辑题:“约翰正好有一个兄弟”的翻译是哪一个?
- python - “é”来自哪个字符集?(Python:带“é”的文件名,如何使用 os.path.exists 、filecmp.cmp、shutil.move?)
- angular - ng g module my-module 只生成 module.ts 文件
- python - 为什么 sklearn cross_val_score 的分数这么低?
- php - 在谷歌云上更新 php 以安装 Debian 4.9