【问题标题】:Why does my task not cancel?为什么我的任务没有取消?
【发布时间】:2016-11-23 13:47:03
【问题描述】:

我正在运行一个进程,该进程会定期 Curls 到 URL 列表以测试这些网站的性能。我的主程序基本上是一个 do...sleep...while 循环,它每 N 秒调用一个函数 runTest(),并通过键盘中断将其终止。精简到基础,我的代码如下:

public void runTest()
{
   if(isRunning)
   {
      return; //Plus some logging that one or more of the tests hasn't completed
   }
   try {
       isRunning = true;
       Curl.GlobalInit((int)CURLinitFlag.CURL_GLOBAL_ALL);
       CancellationTokenSource cts = new CancellationTokenSource(httpTimeoutValueMs * 2);
       foreach (var url in urls)
            taskList.Add(doAsyncCurls(url, cts))
       List<CurlResults> listOfResults = Task.WhenAll(taskList.Select(x => x)).Result.ToList();
       taskList.Clear();
  }
  catch {/*Some exception handling code*/}
  finally { 
    isRunning = false; 
    Curl.GlobalCleanup();
  }
}
private static async Task<CurlResults> doAsyncCurls(string url, CancellationTokenSource cts)
{
    try
    {
      /*Initiate a Curl using libCurl*/
      Easy easy = new Easy();
      easy.SetOpt(CURLoption.CURLOPT_URL, url);
      easy.SetOpt(CURLoption.CURLOPT_DNS_CACHE_TIMEOUT, 0); //Disables DNS cache
      easy.SetOpt(CURLoption.CURLOPT_CONNECTTIMEOUT, httpTimeoutValueMs / 1000); //20sec timeout

      Task t = new Task(() => easy.Perform(), cts.Token);
      t.Start();
      await t;
      return new CurlResults(/*valid results parameters*/);
    } 
    catch (TaskCanceledException)
    { /*Cancellation token invoked*/
        return new CurlResults(/*Invalid results parameters*/);
    }
    catch (Exception e)
    { /*Other exception handling*/ 
        return new CurlResults(/*Invalid results parameters*/);
    }

“doAsyncCurl”函数执行它在锡上所说的,将 http 超时值设置为取消令牌的一半,以便 HTTP 请求应该在取消令牌被调用之前消失,并生成否定结果。我添加了取消令牌以尝试处理我的问题,如下所示。

我让它运行了很长时间 - 一开始,一切正常,但最终(通过定期 runTest() 调用进行数百次,如果不是更多的迭代)似乎其中一项任务卡住了,取消令牌不是t 被调用,并且 curl 测试过程被有效地卡住了。

除了找出哪些 URL 有问题(并且它们都是有效的)之外,还可以做些什么来诊断出了什么问题?还是我一开始就完全错误地构建了平行卷发? (我很感激我可以将新的 Curls 实例化为从上一轮完成的 URL,然后让挂起的 URL 悬空,而不是等待它们全部完成后再开始新批次,但我并不担心这种效率增益)。

【问题讨论】:

  • 传递对令牌的引用并不意味着在任务主体中自动取消任务。您仍然需要定期检查它。 stackoverflow.com/questions/3712939/…
  • 好的 - 所以我已经完成了我的取消令牌实现,但我也很困惑为什么 Curl 也没有超时

标签: c# task libcurl


【解决方案1】:

取消没有自动性。您必须检查取消状态并结束执行或调用CancellationToken.ThrowIfCancellationRequested 来执行取消。 CancellationToken 会告诉您 如果 要求取消;取消本身必须由您完成。

下面这行是你的问题:

Task t = new Task(() => easy.Perform(), cts.Token);

您正在将令牌传递给任务构造函数。如果您的取消发生在任务开始之前,这将对您有所帮助。一旦任务开始,执行的方法负责取消。 easy.Perform() 不知道您的取消令牌,因此永远无法确定是否请求取消。因此,它将运行到最后。

您需要在任务执行期间定期检查您的取消令牌以实现您想要的。

【讨论】:

  • 我应该/可以在这一行之后使用“t.wait(timoutMs)”而不是使用令牌并使用“await t”吗?
  • 是的,你可以,但这不会结束你的任务。它仍然会轮到最后。
  • @JetSetJim 如果你这样做了,那么代码就不会是异步的。你可以构建一个新的任务。您可以使用this 之类的东西来创建一个任务,该任务将在取消令牌被取消时完成,但这不会阻止操作继续执行......无论它想要做什么,如果这对你来说已经足够了。
  • @Sefe - 那么这是否意味着在挂起的任务中实例化的 easy.Perform() 将继续存在直到它完成?如果它在后续调用中一直挂起可能是一个问题,因为没有任何东西可以整理它
  • 如果它抛出一个异常,它会导致任务出错,从而结束它。如果您想跟踪挂起的执行,您必须自己跟踪这些执行并采取相应措施。任务框架本身并没有为您提供终止任务的方法。您可以找出任务正在运行的线程并终止该线程,但这是一个肮脏的解决方案,不推荐。
猜你喜欢
  • 2022-10-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-10-05
相关资源
最近更新 更多