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

时间:2018-10-29 15:26:36

标签: c#

“工人”正在执行一段代码时,我正在关闭整个窗口,并希望将其放置在关闭该窗口上,因为否则将完成其代码。

Task  worker = Task.Factory.StartNew(new Action(() =>
{ 
    // some code here
}

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

  

仅当任务处于完成状态时才可以处置它   (RanToCompletion,错误或已取消)

有人建议我在工作时如何处置它?

2 个答案:

答案 0 :(得分:0)

您需要编写代码,以便您的任务将接受取消令牌。基本上,这只是一个标志,可以由任务中的代码检查,如果更新了该标志,则将提供要处理的逻辑,让任务的逻辑安全地处理如何终止其执行,而不是仅在某些未知状态下停止。在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();
}

要点:

  • 创建CancellationTokenSource的实例。这是一个简单的对象,可以在您希望取消任务时进行通信。

    var taskController = new CancellationTokenSource(); 
    
  • 从此来源获取令牌

    var token = taskController.Token;
    
  • 运行任务,传递对令牌的引用

    var task = Task.Run(() => 
    {
        //...
        , token
    }
    
  • 在任务中添加逻辑以在适当的时候检查此令牌的状态,并适当地处理它。

    if (token.IsCancellationRequested)
    {
        Console.WriteLine("Cancellation Token Detected");
        break;
    }
    
  • 添加逻辑以在您希望取消任务时调用Cancel方法。在上面的代码中,我将其置于Form的FormClosed事件处理程序的逻辑下:

    taskController.Cancel();
    

请参见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);

if (token.IsCancellationRequested)
{
    Console.WriteLine("Cancellation Token Detected");
    break;
}
token.WaitHandle.WaitOne(1000);

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

答案 1 :(得分:-2)

尝试使用取消令牌。

var cancellationTokenSource = new CancellationTokenSource();
var t = Task.Factory.StartNew(() =>
        {
            // Your code here 
        }, cancellationTokenSource.Token).ContinueWith(task =>
          {
              if (!task.IsCompleted || task.IsFaulted)
              {
                  // log error
              }
          }, cancellationTokenSource.Token);

将cancellationTokenSource保持在方便状态,并在您的Close()中取消它