【发布时间】:2021-02-03 12:16:57
【问题描述】:
为了控制我开发的 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);
}
【问题讨论】:
-
还要注意你的类不是线程安全的,我在这里猜测(基于设计)你打算同时调用这个类的方法。如果是这种情况,如果没有锁定机制,从列表中添加和删除是不安全的
-
@pinkfloydx33 OP 可能打算在安装了
SynchronizationContext的 GUI 应用程序中使用此类。在这种情况下,SynchronizationContext将负责同步所有异步延续,方法是在 UI 线程上调用它们。否则事情会复杂得多。您可以看到 here 一个线程安全的CancelableExecution类,以及我必须编写多少代码才能使其正常工作。 -
@TheodorZoulias 我说的是访问
cancellationTokenSources列表。 SyncContext 不会同步对该字段的访问。如果两个添加或添加/删除同时发生,可能会发生不好的事情。最好使用锁定访问或并发集合。只是说 -
@pinkfloydx33 如果所有代码路径都将在同一个线程上运行,怎么可能同时发生两个添加(或添加/删除)?
-
@TheodorZoulias 假设他们是。回到我原来的评论是“if”他们有并发访问要小心。不是他们做
标签: c# ios multithreading delay execution