微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

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

确保所有异常都会出现的最简单方法是将 CallbackAfterDelayasync Task 转换为 async void。此更改将迫使您非常谨慎地编写代码,因为此方法中任何未捕获的异常都将无法处理(不仅仅是未观察到),并且会导致进程崩溃。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。