0

以下は、.net 4.0 で C# winforms アプリケーションを操作しています。

並行して実行されるタスクのリストがあります。それらの実行が完了すると、実行したいコードのブロックがあります (後処理の検証に関連します)。いずれかのタスクが失敗した場合は、例外がコール スタックを UI レベルまでフローするようにします (呼び出す必要があるグローバル例外ハンドラーがあります)。

ContinueWhenAll がブロッキング メソッドではないことを理解しています。また、ContinueWhenAll が新しいタスクを開始していることも知っています。しかし、このタスクを UI と同じスレッドで実行することはできないようです。デバッグとして実行すると、例外が表示されます。ただし、デバッグを行わないと、Continue されたタスクは独自のスレッドで失敗し、例外は処理されずに失われます。

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

だから私は掘り下げました.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 に答える
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 に答える