c# - 尝试在执行代码时处理任务

Iav*_*yov 4 c#

当“worker”正在执行一段代码时,我正在关闭整个窗口,并且我想在关闭该窗口时处理它,因为它正在完成它的代码。

Task  worker = Task.Factory.StartNew(new Action(() =>
{ 
    // some code here
}
Run Code Online (Sandbox Code Playgroud)

不幸的是,当我调用时worker.Dispose()出现Close()异常:

仅当任务处于完成状态(RanToCompletion、Faulted 或 Canceled)时才可以处置任务

有什么建议我可以在它工作时处理它吗?

Joh*_*van 7

您需要编写代码,以便您的任务接受取消令牌。这基本上只是一个可以由任务中的代码检查的标志,如果更新,您将提供要处理的逻辑,让任务的逻辑安全地处理如何终止其执行,而不是简单地停止在某些未知状态。在 LinqPad 中运行以下示例代码应该会给您一个合理的示例来说明所发生的情况:

void Main()
{
    var form = new Form();
    var label = new Label(){Text = string.Format("{0:HH:mm:ss}", DateTime.UtcNow), AutoSize = true};
    form.Controls.Add(label);
    var taskController = new CancellationTokenSource(); 
    var token = taskController.Token;
    var task = Task.Run(() => 
    {
        for (var i=0; i<100; i++)
        {
            var text = string.Format("{0:HH:mm:ss}", DateTime.UtcNow);
            Console.WriteLine(text); //lets us see what the task does after the form's closed
            label.Text = text;
            if (token.IsCancellationRequested)
            {
                Console.WriteLine("Cancellation Token Detected");
                break;
            }
            Thread.Sleep(1000);
        }
    }, token);
    form.FormClosed += new FormClosedEventHandler(
        (object sender, FormClosedEventArgs e) => 
        {taskController.Cancel();}
    );
    form.Show();
}
Run Code Online (Sandbox Code Playgroud)

关键点:

请参阅https://binary-studio.com/2015/10/23/task-cancellation-in-c-and-things-you-should-know-about-it/了解取消任务的好文章/相关方法任务。

边注

在上面的例子中我有点懒。循环的每次迭代我都会检查取消标记;但随后(如果未取消)在循环之前等待 1 秒。由于取消逻辑仅在对if语句求值时才起作用,这意味着我们必须等待 1 秒才能使取消生效,这不太好;如果延迟较大(例如 5 分钟),可能会非常痛苦。这里概述了一种解决方案:https ://stackoverflow.com/a/17610886/361842

即替换

if (token.IsCancellationRequested)
{
    Console.WriteLine("Cancellation Token Detected");
    break;
}
Thread.Sleep(1000);
Run Code Online (Sandbox Code Playgroud)

if (token.IsCancellationRequested)
{
    Console.WriteLine("Cancellation Token Detected");
    break;
}
token.WaitHandle.WaitOne(1000);
Run Code Online (Sandbox Code Playgroud)

有关该方法的文档,请参阅https://learn.microsoft.com/en-us/dotnet/api/system.threading.waithandle.waitone?view=netframework-4.7.2WaitOne