【问题标题】:C# - Kill task after timoutC# - 超时后终止任务
【发布时间】:2020-09-03 03:39:10
【问题描述】:

我有这个方法 ExecuteAllAsync() 它使用字段 List jobs 并异步执行 list 中的所有作业。我想设置一个超时来终止作业并在需要很长时间或不再需要时停止执行。我尝试在以下代码中这样做:


        //Execute all jobs async
        public async void ExecuteAllAsync()
        {
            if (jobs.Count()==0)
                return;
            List<Task> tasks = new List<Task>();
            foreach (Job job in jobs.ToArray())
            {
                int timeout = 500; 

                Task t = Task.Run(() => { 
                     ExecuteJob(job) // Do Work Here
                });
                tasks.Add(t);
                if (!t.Wait(timeout))
                     //Log Timeout Error
            }
            await Task.WhenAll(tasks);
        }

这不起作用。我还尝试将 Thread.Sleep(1000) 放入 ExecuteJob() 函数中以使所有任务超时,结果是只有一些作业超时,而一些作业完全执行。我还使用秒表来检查这项工作需要多长时间,我发现其中一些需要超过 1 秒。

目前超时设置为 500 毫秒,但未来每个任务将有不同的超时值:

int timeout = (int)(job.TimeToKill() - DateTime.Now).TotalMilliseconds

如何在为每个任务设置超时的同时异步执行列表中的所有作业? 提前致谢。

【问题讨论】:

  • thisthis 的可能重复
  • 什么意思?
  • 我的意思是这可能是重复的。并且不清楚必须在哪里设置超时?对于所有任务或每个单独的Task?例如,您为每个设置超时 1 秒,所有作业开始将在 500 毫秒内执行。然后你会在 1.5 秒内完成。或者您想为所有作业执行设置超时,然后终止所有未及时完成的​​作业?顺便说一句,async 方法中的t.Wait() 是错误的,可能会导致在等待时锁定主 UI 线程。
  • ExecuteJob 方法是否接受 CancellationToken 作为参数?如果没有,就没有干净的方法来终止任务。你可能会想到Thread.Abort,但这是no-no,在.NET Core 中也不支持。
  • @RonD 的回答不能满足您的问题?否则请接受一个。

标签: c# multithreading asynchronous async-await timeout


【解决方案1】:

你可以使用这个简单的方法:

public void ExecuteAll() //Note: since ExecuteJob is not async, this method not needs to be async anymore
{
    if (jobs.Count() == 0)
        return;

    int timeout = 500; 

    Parallel.ForEach(jobs, job => ExecuteJob(job, timeout);
}


public void ExecuteJob(Job job, int tiemout) //Your new ExecuteJob overload receiving a Timeout and using a CancellationToken internally
{
    using (var cts = new CancellationTokenSource(timeout))
    {
        foreach (var jobItem in job.ItemsToProcess) //Assuming you have something to do inside our job
        {
            if (cts.Token.IsCancellationRequested)
            {
                // ==> Log Timeout Error; cancel your job 
            }

            // Do your job
        }
    }
}

请注意,您可以简单地使用Parallel.ForEach,它会为您处理多个线程,并根据执行机器中可用的内核数量优化它们

【讨论】:

  • 此代码造成内存泄漏。在每次调用 ExecuteAll 时,您都会创建一个新的 CancellationTokenSource 实例,该实例不会被释放,因此可能会也可能不会释放其不受管理的资源。请注意,很多人只是复制 stackoverflow 的代码。您只需要将对 Parallel.ForEach 的调用包装在带有 using (var cts = new CancellationTokenSource(timeout)) { ... } 的 using 块中
  • 答案已更新为使用 using 作为令牌。感谢@Ackdari 的建议??
【解决方案2】:

你可以使用CancellationToken:

您可以使用CancellationTokenSource 创建一个,然后将超时传递给.CancelAfter(int)

https://docs.microsoft.com/en-us/dotnet/api/system.threading.cancellationtokensource.cancelafter?view=netcore-3.1

确保检查任务中的 cancelToken。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-09-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-06-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多