gpt4 book ai didi

c# - 异步等待 WhenAll 不等待

转载 作者:太空宇宙 更新时间:2023-11-03 15:30:09 25 4
gpt4 key购买 nike

我正在 VS2013、.NET FW 4.5.1 中开发 WinForms 应用程序。这是我的简化代码,其中包含有关结构的内联注释:

// Progress object implementing IProgress<MyProgressData>
var progressCallback = new Progress<MyProgressData>();

// listOfMyList is actually List<List<MyObject>>, which contains list of
// list of MyObject's which will be executed as tasks at once.
// For example, this would be sample structure for list of lists:
// List1
// MyObject1
// MyObject2
// MyObject3
// List2
// MyObject4
// MyObject5
// MyObject6
// List1's and List2's objects would be executed as all tasks at once, but List1 and List2 respectively
// would be executed one after another (because of resources usage inside TASK CODE)
foreach (var myItem in listOfMyList)
{
var myList = myItem.ToList();

// Create a list of tasks to be executed (20 by default; each taking from 30-60 seconds)
// Here cs is actually MyObject
var myTasks = myList.Select(cs => Task.Run(async () =>
{
// TASK CODE (using cs as an input object and using "cancellationToken.ThrowIfCancellationRequested();" inside execution to cancel executing if requested)
}, cancellationToken));

await Task.WhenAll(myTasks); // Wait for all tasks to finish

// Report progress to main form (this actually calls an event on my form)
await Task.Run(() => progressCallback.Report(new MyProgressData() { props }), CancellationToken.None);
}

如您所见,我构造了进度对象,然后我得到了列表列表。顶级列表中的每个项目都应该以序列化的方式执行(一个接一个)。每个项目的列表元素,应该以任务的形式一次执行。到目前为止一切顺利,所有任务都开始了,甚至 WhenAll 都在等待它们。或者至少我是这么认为的。我已将日志记录放入相关方法中,以显示代码执行情况。事实证明,当进度逻辑(在底部)正在执行时,foreach 循环开始执行另一批任务,这是不应该的。我在这里错过了什么吗?进度代码是否不阻塞或等待 Report 方法完成执行。也许我错过了关于异步/等待的事情。使用 await 我们确保代码在方法完成之前不会继续?它不会阻塞当前线程,但它也不会继续执行?我的 foreach 循环是否有可能(自从发生以来,它可能是)继续执行,同时进度报告仍在进行中?

此代码驻留在异步方法中。它实际上是这样调用的(假设此方法是 async MyProblematicMethod()):

while (true)
{
var result = await MyProblematicMethod();
if (result.HasToExitWhile)
break;
}

从 MyProblematicMethod 开始的每个方法都使用 await 来等待异步方法,并且不会被多次调用。

最佳答案

根据 Glorin 关于 IProgress.Report 在触发事件处理程序后立即返回的建议,我创建了 Progress 类的精确副本,它使用 synchronizationContext.Send 而不是 Post:

public sealed class ProgressEx<T> : IProgress<T>
{
private readonly SynchronizationContext _synchronizationContext;
private readonly Action<T> _handler;
private readonly SendOrPostCallback _invokeHandlers;

public event EventHandler<T> ProgressChanged;

public ProgressEx(SynchronizationContext syncContext)
{
// From Progress.cs
//_synchronizationContext = SynchronizationContext.CurrentNoFlow ?? ProgressStatics.DefaultContext;
_synchronizationContext = syncContext;
_invokeHandlers = new SendOrPostCallback(InvokeHandlers);
}

public ProgressEx(SynchronizationContext syncContext, Action<T> handler)
: this(syncContext)
{
if (handler == null)
throw new ArgumentNullException("handler");
_handler = handler;
}

private void OnReport(T value)
{
// ISSUE: reference to a compiler-generated field
if (_handler == null && ProgressChanged == null)
return;
_synchronizationContext.Send(_invokeHandlers, (object)value);
}

void IProgress<T>.Report(T value)
{
OnReport(value);
}

private void InvokeHandlers(object state)
{
T e = (T)state;
Action<T> action = _handler;
// ISSUE: reference to a compiler-generated field
EventHandler<T> eventHandler = ProgressChanged;
if (action != null)
action(e);
if (eventHandler == null)
return;
eventHandler((object)this, e);
}
}

这意味着 ProgressEx.Report 将等待方法完成,然后再返回。也许不是所有情况下的最佳解决方案,但在这种情况下它对我有用。

要调用它,只需使用 SynchronizationContext.Current 创建 ProgressEx作为构造函数的参数。但是,它必须在 UI 线程中创建,因此会传入正确的 SynchronizationContext。例如new ProgressEx<MyDataObject>(SynchronizationContext.Current)

关于c# - 异步等待 WhenAll 不等待,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34068781/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com