无法在 UI 线程中执行 ContinueWhenAll
本文关键字:执行 ContinueWhenAll 线程 UI | 更新日期: 2023-09-27 18:31:05
以下内容是在.net 4.0中使用C# winforms应用程序进行操作
的我有一个并行执行的任务列表。一旦它们的执行完成,我就会想要执行一个代码块(与后处理验证有关)。如果任何任务失败,我希望异常将调用堆栈向上流动到 UI 级别(我有一个需要调用的全局异常处理程序)。
我知道ContinueWhenAll不是一种阻止方法。我也知道ContinueWhenAll正在启动一项新任务。 但我似乎无法使此任务在与 UI 相同的线程中运行。作为调试运行时,异常可见。但是,如果不进行调试,则 Continue'd 任务在其自己的线程中失败,并且异常将未处理并丢失。
我认为我对TaskContinuationOptions.ExecuteSyncly的使用是原因(MSDN"延续将在导致先前任务转换到其最终状态的同一线程上运行")。 无论如何我可以在 UI 线程上强制执行吗?还是我使用了错误的工具来完成工作?
//Being called in the UI thread
var tasks = new List<Task>();
foreach (var item in workList)
{
tasks.Add(item.DoWorkAsync);
}
var context = TaskScheduler.FromCurrentSynchronizationContext();
Task.Factory.ContinueWhenAll(tasks.ToArray(), LoadComplete, CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, context);
...
private void LoadComplete(Task[] tasks)
{
var errors = (from t in tasks where t.Exception != null select t.Exception);
if (errors.Count() > 0)
throw new AggregateException(errors.ToArray());
}
我刚刚拼凑了一个非常快速的应用程序,以查看 .net 4 和 4.5 中会发生什么,经过 10 次测试后,所有继续代码都在 UI 线程上运行
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
var tasks = new List<Task>();
Console.WriteLine("Main Thread" + System.Threading.Thread.CurrentThread.ManagedThreadId);
tasks.Add(new Task(() => Console.WriteLine("Task 1:" + System.Threading.Thread.CurrentThread.ManagedThreadId)));
tasks.Add(new Task(() => Console.WriteLine("Task 1:" + System.Threading.Thread.CurrentThread.ManagedThreadId)));
var context = TaskScheduler.FromCurrentSynchronizationContext();
Task.Factory.ContinueWhenAll(tasks.ToArray(), LoadComplete, CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, context);
tasks.ForEach(task => task.Start());
Console.ReadLine();
}
private void LoadComplete(Task[] tasks)
{
Console.WriteLine("Completion Task" + System.Threading.Thread.CurrentThread.ManagedThreadId);
}
}
所以我做了一些挖掘,我使用 ContinueWhenAll 的方式对于我所拥有的场景是正确的(任务与 IO 相关)。
.net 4.5 有各种好的工具,例如 async/await,但我们现在需要将这个项目保留在 4.0 中。 因此,确保 LoadComplete 在 UI 线程中运行的最简单方法是 BeingInvoke
private void LoadComplete(Task[] tasks)
{
//Invoke on UI thread
if (this.InvokeRequired)
this.BeginInvoke((MethodInvoker)delegate
{
var errors = (from t in tasks where t.Exception != null select t.Exception);
if (errors.Count() > 0)
throw new AggregateException(errors.ToArray());
});
}