为啥我的循环中的异步委托没有等待
Posted
技术标签:
【中文标题】为啥我的循环中的异步委托没有等待【英文标题】:Why is my async delegate in my loop not awaiting为什么我的循环中的异步委托没有等待 【发布时间】:2018-06-12 11:10:03 【问题描述】:预期: 我需要定期运行任务。我希望任务运行,然后等待一些延迟,然后再次运行任务......等等。
实际: 我发现 _loadingMethod 被调用/重新输入。我希望转发器中的 while 循环在 await foo() 语句完成之前不会回来。
问题: 有任何想法吗?我觉得我一直在正确地等待调用堆栈。也许我错过了 lambda 或某处的委托中的某些内容..?
internal static CancellationTokenSource Repeat(Func<Task> foo, TimeSpan interval, int skipFirstFewIntervals = 0)
CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
TaskWrapper.RunYetThrowOnMainThread( // kick off the loop
async () =>
await Task.Delay(skipFirstFewIntervals * interval.Milliseconds); // initial delay (if any)
while (await DelayOrCancel(interval, cancellationTokenSource.Token)) // loop forever or until cancel
try
await foo();
catch (Exception ex)
string msg = $"Failed while repeating: foo.Method.Name";
throw new Exception(msg, ex);
;
);
return cancellationTokenSource;
private static async Task<bool> DelayOrCancel(TimeSpan interval, CancellationToken token)
bool keepGoing;
try
await Task.Delay(interval, token); // this throws on cancel
keepGoing = true;
catch (OperationCanceledException)
keepGoing = false; // kill the loop on cancel
return keepGoing;
TaskWrapper 类在下面。
internal static class TaskWrapper
internal static Task RunYetThrowOnMainThread(Action foo, CancellationToken cancellationToken = default(CancellationToken))
=> RunYetThrowOnMainThread(() => Task.Run(foo), cancellationToken);
internal static Task RunYetThrowOnMainThread(Func<Task> foo, CancellationToken cancellationToken = default(CancellationToken))
return Task.Run(async () =>
try
await foo();
catch (Exception ex)
string msg = $"Failed at CPU bound task: foo.Method.Name";
ex.ThrowOnMainThread(msg);
;
);
这是 ThrowOnMainThread ext 方法的 Throw。这样即使我没有观察任务,我也可以强迫前任浮出水面。
internal static void ThrowOnMainThread(this Exception ex, string msg) => Xamarin.Forms.Device.BeginInvokeOnMainThread(() => throw new Exception(msg, ex));
在调用层它看起来像这样。
internal CancellationTokenSource Start()
return Repeater.Repeat(LoadAsync, Cfg.LoadingInterval);
您可以看到令牌源被向上传递调用堆栈。
在顶层我有类似的东西。
_incidentLoaderCanceller = _incidentLoader.Start();
其中 _incidentLoaderCanceller 是 CancellationTokenSource。
_incidentLoader 是一个
internal class Loader<TData>
private readonly Func<TData, Task<bool>> _loadingMethod;
private readonly IEnumerable<Func<Exception, bool>> _exceptionHandlers;
public Loader(Func<TData, Task<bool>> loadingMethod, IEnumerable<Func<Exception, bool>> exceptionHandlers)
_loadingMethod = loadingMethod.ThrowIfDefaultT();
_exceptionHandlers = exceptionHandlers.ThrowIfDefaultT();
internal CancellationTokenSource Start()
return Repeater.Repeat(LoadAsync, Cfg.LoadingInterval);
private async Task LoadAsync()
... // some looping over this guy LoadAsync(TData data)
...
private async Task<bool> LoadAsync(TData data)
bool result = false;
try
result = await _loadingMethod(data).ConfigureAwait(false);
catch (Exception ex)
ex.HandleOrRethrow(_exceptionHandlers);
return result;
【问题讨论】:
您传递给TaskWrapper.RunYetThrowOnMainThread
的委托(隐式)是异步无效的。使方法 RunYetThrowOnMainThread 采用 FuncRunYetThrowOnMainThread
返回的Task
,因此在该任务完成时改变,或者它的结果是什么,当没有人在看它时并不重要。虽然我个人不喜欢这种设计,但实际上这里调用堆栈的顶部是 async void
设计。
我只是指出我立即看到的完全错误的事情,而没有花时间深入研究这个令人费解的例子。如果有完整的解决方案,我会发布答案。
@dFlat 获得真正帮助的唯一方法是将其减少到 MCVE。
【参考方案1】:
我发现并解决了这个问题。原来我的错误处理程序错误地将特定异常标记为不应该处理的异常。上面的代码对我有用。很难让它开发和测试。希望它会帮助别人。
【讨论】:
以上是关于为啥我的循环中的异步委托没有等待的主要内容,如果未能解决你的问题,请参考以下文章