4

Parallel.ForEach ループを実装して何らかの作業を行っていましたが、キャンセルを処理していると思っていたときに、未処理の例外が原因で問題が発生しました。

問題を把握するために、winform で簡単なテスト設定を行いました。開始ボタン、キャンセル ボタン、出力用のラベルがあります。

コード:

public partial class Form1 : Form
{
    CancellationTokenSource cts = new CancellationTokenSource();

    public Form1()
    {
        InitializeComponent();
    }

    private async void button1_Click(object sender, EventArgs e)
    {
        output.Text = "Running";

        try
        {
            var runTask = Task<string>.Factory.StartNew(() => Run());
            await runTask;
            this.output.Text = runTask.Result;
        }
        catch(Exception ex)
        {
            throw ex;
        }
    }

    private string Run()
    {
        int useThreads = Environment.ProcessorCount - 2 < 1 ? 1 : Environment.ProcessorCount - 2;

        ParallelOptions options = new ParallelOptions() { MaxDegreeOfParallelism = useThreads, CancellationToken = cts.Token };

        options.CancellationToken.Register(() => ActionOnCancel());

        List<int> somelist =new List<int>();

        for(int i = 0; i < 100; i++)
            somelist.Add(i);

        Parallel.ForEach(somelist, options, (row, loopstate) =>
        {
            if(loopstate.ShouldExitCurrentIteration || loopstate.IsExceptional)
                loopstate.Stop();

            Thread.Sleep(1000);

        });

        return "Done";
    }

    private void ActionOnCancel()
    {
        output.Text= "Cancelled";
    }

    private void button2_Click(object sender, EventArgs e)
    {
        cts.Cancel();
    }

プログラムを実行してキャンセル ボタンを押すと (button2_Click をトリガーするため)、次のエラーが発生し続けます。

タイプ 'System.OperationCanceledException' の例外が mscorlib.dll で発生しましたが、ユーザー コードで処理されませんでした

追加情報: 操作はキャンセルされました。

この例外のハンドラがあれば、プログラムは安全に続行できます。

また、デバッガーは Parallel.ForEach セクションを強調表示します。しかし、なぜ???CancellationToken を介してキャンセルを正しく処理したと思います。

ex の例外メッセージでは、明確な説明が得られません:「{"操作がキャンセルされました。"}」ええと...ええ....それが意図されていました...

私は何を見落としていますか?よろしく、

マティス

4

2 に答える 2

8

この例外は常にスローされます。CancellationTokenSource で cancel メソッドが呼び出された場合、並列タスクにアクセスするときに OperationCanceledException を処理する必要があります。

try
{
    Parallel.ForEach(somelist, options, (row, loopstate) =>
    {
        if(loopstate.ShouldExitCurrentIteration || loopstate.IsExceptional)
            loopstate.Stop();

        Thread.Sleep(1000);

    });
}
catch (OperationCanceledException)
{
    // Handle the cancelled Task
}
于 2012-12-14T15:43:45.247 に答える
1

CancellationToken.Register() は、キャンセルにコールバックを追加する方法を提供しますが、OperationCanceledException に関して、それを「処理」しません。

Run() メソッドにも try/catch ブロックを配置することをお勧めします。

于 2012-12-14T15:41:29.050 に答える