【问题标题】:How to stop async Task after a certain period of time如何在一段时间后停止异步任务
【发布时间】:2021-01-27 08:15:26
【问题描述】:

我正试图在一段时间后停止 C# 中的任务。

对于我的代码:Task.Delay().Wait() 应该只代表 Task 所做的一些工作。

我的代码:

public static void Main()
{
    Console.WriteLine("starting app");
    try
    {
        Console.WriteLine("before");
        DoStuff(1000);
        Console.WriteLine("after");
    }
    catch
    {
        Console.WriteLine("TIMEOUT");
    }
    Console.WriteLine("Main finished wait 5 sec now");
    Task.Delay(10000).Wait();
    Console.WriteLine("Closing app now");
}

public static async Task DoStuff(int time)
{
    Task real = Task.Run(()=>
    {
        Task.Delay(2000).Wait();
        Console.WriteLine("Task Running");
        Task.Delay(2000).Wait();
        Console.WriteLine("still running");
        Task.Delay(2000).Wait();
        Console.WriteLine("Not dead yet");
        Task.Delay(1000).Wait();
        Console.WriteLine("Task done");
        Task.Delay(5000);
    });

    bool success = real.Wait(time);
    if ( success )
    {
        Console.WriteLine("Task finished in time");
        await real;
    }
    else
    {
        Console.WriteLine("Task did not finish");
        real.Dispose();
        throw new TimeoutException("The task took too long");
    }
}

我已经尝试使用取消令牌,但我没有循环来检查每次迭代的令牌。所以我尝试用Task.Wait(time) 来做这件事,我得到了正确的信息,但是在使用Task.Dispose() 方法后任务并没有停止。我得到以下输出:

所以我得到了当前的输出,但任务继续在后面运行..关于如何解决这个问题的任何想法?

【问题讨论】:

  • DoStuff的所有任务上调用Wait是非常错误的
  • @TheodorZoulias 鉴于每个.Wait()严重损坏,我不确定这是否值得特别注意:)
  • @MarcGravell “可怕”是一个强词。我称之为“不是最佳的”。 ??????
  • Alex 所以如果我理解正确你想以非合作方式取消 DoStuff 异步方法?如果是这种情况,那么简短的回答是“你不能”。 .NET 中的取消是cooperative。只有aborting threads 可以进行非合作取消,这很危险,因为它会破坏程序的内部状态,甚至在较新版本的 .NET 中也不支持。
  • 您无法真正取消并非设计为取消的Task。您可以做的是取消等待任务。换句话说,任务将继续运行,但您将忽略它,您的代码不会等待它完成。如果您对此感到满意,您可以查看此question 以获取解决方案。有人发布了一个方便的WaitOrCancel 方法,您可以使用。

标签: c# asynchronous async-await task


【解决方案1】:

首先:永远不要Wait() 处理任务(除非你知道他们已经完成了);使用await。至于超时:CancellationTokenSource可以延时后超时,如下。

请注意,取消令牌不会中断代码-您(或其他代码,如Task.Delay)需要检查取消(对于例如,ThrowIfCancellationRequested()),或者您需要使用 Register(...) 添加一个回调,该回调将在取消时调用

using System;
using System.Threading;
using System.Threading.Tasks;

static class P
{

    public static async Task Main()
    {
        Console.WriteLine("starting app");
        try
        {

            Console.WriteLine("before");
            await DoStuffAsync(1000);
            Console.WriteLine("after");
        }
        catch
        {
            Console.WriteLine("TIMEOUT");
        }
        Console.WriteLine("Main finished wait 5 sec now");
        await Task.Delay(5000);
        Console.WriteLine("Closing app now");
    }
    public static async Task DoStuffAsync(int time)
    {
        using var cts = new CancellationTokenSource(time); // assuming here that "time" is milliseconds
        Task real = Task.Run(async () =>
        {
            await Task.Delay(2000, cts.Token);
            Console.WriteLine("Task Running");
            await Task.Delay(2000, cts.Token);
            Console.WriteLine("still running");
            await Task.Delay(2000, cts.Token);
            Console.WriteLine("Not dead yet");
            await Task.Delay(2000, cts.Token);
            Console.WriteLine("Task done");
            await Task.Delay(2000, cts.Token);
        });

        bool success;
        try
        {
            await real;
            success = true;
        }
        catch (OperationCanceledException)
        {
            success = false;
        }

        if (success)
        {
            Console.WriteLine("Task finished in time");
        }
        else
        {
            Console.WriteLine("Task tool too long");
        }
    }
}

请注意,您也可以将取消令牌传递给Task.Run,但这只是在执行回调之前检查 - 而且没有通常会造成很大的延迟,所以...通常不值得。

附加说明:如果您想确定究竟是什么被取消(即OperationCanceledException 来自cts):

catch (OperationCanceledException cancel) when (cancel.CancellationToken == cts.Token)

【讨论】:

  • 请注意 OP:我个人更喜欢 CancellationTokenSource 的 ctor 和 TimeSpan 以提高可读性。但这与功能无关。
  • 如你所说,取消令牌将在执行 Task.Run 之前检查,但我想在任务运行时取消任务,而不是在开始之前。我认为注册是解决这个问题的方法,对吧?
  • @AlexIsMyName 问题中没有显示任何适合Register() 的内容。正如我所说:由 您的 代码来检查是否取消,除非您可以打包内置检查(例如在 Task.Delay(...) 中)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多