首页 > 解决方案 > 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);
}

标签: c#iosmultithreadingdelayexecution

解决方案


_ = Delayer.CallbackAfterDelay任务以即发即弃的方式启动,因此如果您的实现中有任何错误,您将不会收到通知。而且我已经可以看到一个错误:awaitTask.Delay可能导致OperationCanceledException未捕获的结果,因此在这种情况下callback?.Invoke();不会调用,导致关联的cancellationTokenSource不会从列表中删除。

确保出现所有异常的最简单方法是将CallbackAfterDelayfrom转换async Taskasync void. 此更改将迫使您在编写代码时非常小心,因为此方法中的任何未捕获的异常都将未被处理(不仅仅是未观察到),并且会使进程崩溃。


推荐阅读