0

完了するのに約1分かかるタスクとサブタスクの複雑なツリーを実行するバックグラウンドワーカースレッドを生成するUIがあります。

要件は、バックグラウンドワーカータスクが開始されたらキャンセルできる必要があることです。

現時点では、私の解決策は素朴で、コードを混乱させています。UIでキャンセルボタンを押すと、キャンセルトークンが設定されます。ワーカースレッドは定期的に(タスク間で)このトークンをポーリングし、設定されている場合は終了します。

void ThreadWorkerHandler(CancelToken cancelToken)
{
    DoTask1(cancelToken);
    if (cancelToken.IsSet)
        return;
    DoTask2(cancelToken);
    if (cancelToken.IsSet)
        return;
    DoTask3(cancelToken);
    if (cancelToken.IsSet)
        return;
    DoTask4(cancelToken);
}

void DoTask2(CancelToken cancelToken)
{
    DoSubTask2a();
    if (cancelToken.IsSet)
        return;
    DoSubTask2b();
    if (cancelToken.IsSet)
        return;
    DoSubTask2c();
    if (cancelToken.IsSet)
        return;
}

より良い解決策はありますか?私は、チェックインを自動的にペッパーし、条件が満たされた場合に内部例外を自動的に発生させるSoLongAsステートメントのようなものを探していました。これは、ループの最後で内部的にキャッチされます。例:

void ThreadWorkerHandler(CancelToken cancelToken)
{
    SoLongAs (canelToken.IsSet == false)
    {
        DoTask1(cancelToken);
        DoTask2(cancelToken);
        DoTask3(cancelToken);
        DoTask4(cancelToken);
    }
}

しかし、それは何らかの理由で機能しないと思います。さらに重要なことに、このようなものが実際に存在するのではないかと疑っています。そうでない場合、私が現在使用しているよりもこのシナリオを処理するためのより良い方法はありますか?ありがとう。

4

3 に答える 3

2

作業を表すデリゲートのコレクションがある場合は、コードスニペットに非常に近いものを取得できます。意図した構文よりも少しオーバーヘッドがありますが、重要な点は、行ごとのオーバーヘッドではなく、一定のオーバーヘッドあるということです。

List<Action> actions = new List<Action>()
{
    ()=> DoTask1(cancelToken),
    ()=> DoTask2(cancelToken),
    ()=> DoTask3(cancelToken),
    ()=> DoTask4(cancelToken),
};

foreach(var action in actions)
{
    if (!cancelToken.IsSet)
        action();
}
于 2013-02-28T18:02:17.070 に答える
1

を使用できますCancellationToken.ThrowIfCancellationRequested()。トークンが設定されている場合、これは例外をスローします。

また、の使用を検討してTPL Tasksください。フレームワークは継続を呼び出す前に状態のチェックを処理するため、すべてのサブタスクを同じもので次々にチェーンできますCancellationToken。これにより、コードが簡素化されます。TPLToken

コードは次のようになります。

Task.Factory.StartNew(DoTask1, cancelationToken)
            .ContinueWith(t => DoTask2(), cancelationToken)
            .ContinueWith(t => DoTask3(), cancelationToken)
            .ContinueWith(t => DoTask4(), cancelationToken)

DoTask<i>を除く他の例外をスローしないと仮定して、このソリューションに注意OperationCanceledExceptionしてください。

注2ThrowIfCancellationRequested()タスク/サブタスク本体内で呼び出す必要はありません。TPL継続を呼び出す前に、トークンの状態を自動的にチェックします。ただし、このメソッドを使用して、タスク/サブタスクの実行を中断できます。

于 2013-02-28T18:06:05.090 に答える
0

Servyのアイデアはとても良いです。私はそれを盗んで(彼の功績を称えて!)、の拡張メソッドでそれを使用する方法を示していますList<Action>。「可愛すぎる」と思っている人は誰でも理解できますが、ある程度の優雅さがあると思います。

これは、拡張メソッドの使用方法を示す抜粋です。拡張機能は、アクションデリゲートのリストを取得し、Servyのアイデアに従って、終了またはキャンセルされるまで各デリゲートを順番に実行します。

private static bool test(CancellationToken cancelToken)
{
    return new List<Action> 
    { 
        doTask1,
        doTask2,
        doTask3,
        doTask4,
        () => Console.WriteLine("Press a key to exit.")
    }
    .Run(cancelToken);
}

そして、これが全体のサンプルです:

    using System;
    using System.Collections.Generic;
    using System.Threading;
    using System.Threading.Tasks;

    namespace ConsoleApplication2
    {
        internal class Program
        {
            private static void Main(string[] args)
            {
                CancellationTokenSource cancelSource = new CancellationTokenSource();

                Console.WriteLine("Press any key to interrupt the work.");
                var work = Task<bool>.Factory.StartNew(() => test(cancelSource.Token));
                Console.ReadKey();
                cancelSource.Cancel();
                Console.WriteLine(work.Result ? "Completed." : "Interrupted.");
            }

            private static bool test(CancellationToken cancelToken)
            {
                return new List<Action> 
                { 
                    doTask1,
                    doTask2,
                    doTask3,
                    doTask4,
                    () => Console.WriteLine("Press a key to exit.")
                }
                .Run(cancelToken);
            }

            private static void doTask1()
            {
                Console.WriteLine("Task 1 Working...");
                Thread.Sleep(1000);
                Console.WriteLine("...did some work.");
            }

            private static void doTask2()
            {
                Console.WriteLine("Task 2 Working...");
                Thread.Sleep(1000);
                Console.WriteLine("...did some work.");
            }

            private static void doTask3()
            {
                Console.WriteLine("Task 3 Working...");
                Thread.Sleep(1000);
                Console.WriteLine("...did some work.");
            }

            private static void doTask4()
            {
                Console.WriteLine("Task 4 Working...");
                Thread.Sleep(1000);
                Console.WriteLine("...did some work.");
            }
        }

        public static class EnumerableActionExt
        {
            public static bool Run(this IEnumerable<Action> actions, CancellationToken cancelToken)
            {
                foreach (var action in actions)
                {
                    if (!cancelToken.IsCancellationRequested)
                    {
                        action();
                    }
                    else
                    {
                        return false;
                    }
                }

                return true;
            }
        }
    }
于 2013-02-28T18:44:16.127 に答える