一个类型为'System.OperationCanceledException'的异常发生了。

8

我正在实现一个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”中没有给我任何清晰度:“{"The operation was canceled."}”,嗯...是的...那就是我的意图......
我忽略了什么吗? 谢谢,
Matthijs

看看这个 https://dev59.com/Y2s05IYBdhLWcg3wQvyq 。它帮助我理解了。 - Belurd
2个回答

10

无论何时都会抛出此异常。当在CancellationTokenSource上调用取消方法时,您必须处理操作已取消异常以访问并行任务。

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

        Thread.Sleep(1000);

    });
}
catch (OperationCanceledException)
{
    // Handle the cancelled Task
}

1

CancellationToken.Register() 提供了一种在取消时添加回调的方法,但并没有在 OperationCanceledException 方面进行“处理”。

我建议您在 Run() 方法中也放置 try/catch 块。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接