【问题标题】:Using Task.Run in c# code to run the code for a specific duration [duplicate]在 c# 代码中使用 Task.Run 将代码运行特定持续时间 [重复]
【发布时间】:2020-04-07 04:37:53
【问题描述】:

我有一段用 c# 编写的对数据进行一些处理的代码。

string status;
log.Info("Starting the Process");
StartProcessing(); // takes 10-12 mins usually 

var task = Task.Run(() =>
{
    do
    {
        status = GetStatus();
        log.Info("Status inside loop : " + status);
    } while (status != "Complete")
});

if (task.Wait(TimeSpan.FromMinutes(Convert.ToDouble(15))))
{
    log.Info("Processing finished");
}
else
{
    log.Info("Process did not complete in 15 mins");
    log.Info("Stopping the Process");
    StopProcessing(); // this takes 2-3 mins usually.
    log.Info("Process stopped");
}

StartProcessing() 方法实际上是对数据进行一些后台处理。它不返回任何值或等待方法完成。所以我们添加了一个do-while循环来检查处理的状态。如果状态为 complete ,则退出循环并继续进行。

现在要求已更改为处理超时。如果处理时间超过 5 分钟,那么我们必须停止该过程。因此,我将代码包装在 Task.Run 中,如上所示,并编写了一个 if else 条件来检查时间。

这似乎没有按预期工作,因为当我运行我的代码时,这是我得到的日志信息。

Starting the Process  
Status inside loop : Processing
Status inside loop : Processing
Status inside loop : Processing
Status inside loop : Processing --> this line is repeated multiple times with in 15 mins.  
Process did not complete in 15 mins.  
Stopping the Process  
Status inside loop : Processing  
Status inside loop : Processing  
Status inside loop : Processing --> why is it going back to do while after coming out ?
Process stopped 

即使在出来之后,执行也会回到执行状态。我在这里做错了什么吗?
任何建议都非常有帮助。

【问题讨论】:

  • 不是一个直接的答案,但您可能会发现这个库很有用:github.com/App-vNext/Polly
  • 问题在于您只是在等待任务,而不是取消它。网站上有许多现有问题的答案,这些问题涉及如何正确取消一项任务。查看标记为一对的重复项。

标签: c# task


【解决方案1】:

您需要添加在给定时间后终止后台任务的代码。为此,您最好将CancellationToken 引入您的处理任务。执行以下操作:

CancellationTokenSource source = new CancellationTokenSource(TimeSpan.FromMinutes(5));
StartProcessing(source.Token);

然后,在您的处理任务中,定期调用token.IsCancellationRequested,如果这是真的,则中止。

【讨论】:

  • 很好,不过您应该直接致电ThrowIfCancellationRequested。如果正在使用链式任务/延续,则尤其重要
  • @PMF ,非常感谢您的回复。我们无法控制 StartProcessing() 、 GetStatus() 、 StopProcessing() 方法,因为这些方法是用 dll 编写的,我们没有它的源代码。所以我不能传递任何参数或检查方法内的任何内容。
  • @Crazy: “我不能传递任何参数或检查方法内部的任何东西”——那么你将不得不忍受这种情况。没有它的合作,就无法安全地中断任务。
  • @PeterDuniho 我认为OP的问题更多是关于取消Task.Run返回的任务,OP认为,从问题的外观来看,.Wait(timeout)返回后,任务应该自动停止。
  • @CrazyCoder:那么你无能为力。您可以尝试以某种方式获取(可能)在那里创建的线程的句柄并杀死它,但这很可能会导致问题。因此,您可能应该要求该 DLL 的创建者介绍中止处理的可能性。
【解决方案2】:

如果您可以访问StartProcess 的源代码,那么就像@PMF 所说,将CancellationToken 传递给方法是有效的方法。

否则,您可以使用TaskCompletionSourceCancellationTokenSource 创建自己的可取消任务:

Task<bool> ProcessAsync(TimeSpan timeout)
{
    var tcs = new TaskCompletionSource<bool>();
    var cts = new CancellationTokenSource();

    // If task canceled, set to return false
    cts.Token.Register(() => tcs.TrySetResult(false));

    // Cancel after timeout
    cts.CancelAfter(timeout);

    // Start process.
    StartProcess();

    // Start waiting until complete or canceled.
    Task.Run(() =>
    {
        while (GetStatus() != "Complete" && !cts.IsCancellationRequested)
        {
        }

        if (cts.IsCancellationRequested)
        {
            // Task has been canceled due to timeout.
            tcs.TrySetResult(false);
        }
        else
        {
            // Task done. Status is "Completed".
            tcs.TrySetResult(true);
        }
    });

    return tcs.Task;
}

然后等待ProcessAsync:

// Or ProcessAsync(TimeSpan.FromMinutes(5)).Result for sync method
var isCompleted = await ProcessAsync(TimeSpan.FromMinutes(5));
if (!isCompleted)
{
    StopProcess();
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-04-18
    • 1970-01-01
    • 2012-04-29
    • 1970-01-01
    相关资源
    最近更新 更多