0

以下是在 .net 4.0 中使用 C# winforms 应用程序进行操作

我有一个并行执行的任务列表。执行完成后,我要执行一段代码(与后处理验证有关)。如果任何任务失败,我希望异常沿调用堆栈向上流动到 UI 级别(我有一个需要调用的全局异常处理程序)。

我了解 ContinueWhenAll 不是阻塞方法。我也知道 ContinueWhenAll 正在启动一项新任务。但我似乎无法让这个任务在与 UI 相同的线程中运行。以 Debug 运行时,异常可见。但是如果不进行调试,Continue'd 任务会在其自己的线程中失败,并且异常未处理并丢失。

我认为我对TaskContinuationOptions.ExecuteSynchronously的使用是原因(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());
}
4

2 回答 2

1

我刚刚拼凑了一个非常快速的应用程序,看看在 .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);
    }
}
于 2013-09-11T15:39:52.827 回答
1

所以我做了一些挖掘,我使用 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());

                });
    }
于 2013-09-13T09:30:06.507 回答