【问题标题】:How to make sure a task is completed in c#如何确保在 C# 中完成任务
【发布时间】:2021-10-14 18:33:03
【问题描述】:

我需要执行这样的任务(我无法控制该任务的源代码)

public Task DoSomething()

有时该任务无法完成。而且这种方法不会引发任何异常。在这种情况下,我想再次执行该任务,直到它完成。 为了做到这一点,我尝试这样:

Task task;
do {
    task = Task.Run(async () => await DoSomething();
    await Task.Delay(300);
}
while (!task.IsCompleted);

正确吗?有没有更好的办法?

【问题讨论】:

  • 使用await 运算符。
  • 不,这绝对不正确,当前一个任务没有在 300 毫秒内完成时,您开始一个新任务。
  • > “正确吗?” - 没有
  • “尚未完成”和“故障”之间存在巨大差异 - 现在,您可能会同时启动数百个 DoSomething() 调用(一个新的每 300 毫秒一次),这听起来像是一种让性能变得越来越差的灾难性方法,以及更长时间运行的操作。 DoSomething() 这里是什么?
  • 试试:await DoSomething();

标签: c# .net asynchronous


【解决方案1】:

你可能想要这样的东西:

  // Trying to complete task again and again... 
  // you may want to give some number of attempt to complete, say 
  // for (int attempt = 1; attempt <= 3; ++i) {
  while (true) {
    // Let task 300 ms to completet before it'll be canceled
    using (CancellationTokenSource cts = new CancellationTokenSource(300)) {
      try {
        await DoSomething(cts.Token);

        // Task Succeeded, break the loop
        break;
      }
      catch (TaskCanceledException) {
        // Task Cancelled, keep on looping
        ;
      }
    }
  }

请注意,您应该在DoSomething 中获得一些支持

   async Task DoSomething(CancellationToken token)

而不是

   async Task DoSomething()

因为我们应该知道如何取消它。如果您没有

   async Task DoSomething(CancellationToken token)

或类似的签名(带有CancellationToken),残酷的事实是您无法取消。您可以尝试等待(如果任务未完成,则将其忘记):

   // Since we are going to forget running tasks, let's
   // restrict number of attempts 
   for (int attempt = 1; attempt <= 3; ++attempt) {
     Task task = DoSomething();

     if (await Task.WhenAny(task, Task.Delay(300)) == task) {
       // task has been completed before timeout
       break;
     } 

     // we forget task (alas, we can't cancel it) and try again
   }

【讨论】:

  • 我不明白这一点:DoSomething() 不接受任何参数,并且 OP 说他无法控制其源代码。在您的答案代码示例中,您将取消令牌放入 DoSomething()
  • @user2190492:谢谢!所以问题是如果有async Task DoSomething(CancellationToken token) 过载,如果是我们可以使用取消;如果不是 - 我们只能忘记之前的尝试,然后再次运行DoSomething()
  • 感谢您的评论。该任务无法取消。但我会使用“尝试等待并忘记”的方式发帖。
【解决方案2】:

你应该直接用await调用异步方法。

如果发生错误,应该抛出异常。

你的代码应该变成这样。

bool repeat = false;
do {
    try 
    { 
        await DoSomething(); 
        repeat = false;
    }
    catch
    {
        repeat = true;
    }
    if (repeat)
        await Task.Delay(300); 
}
while (repeat); // better with some kind of timeout or attempts counter...

此外,IsCompleted 还包括FaultedCancelled。如果您想使用您的方法,您需要使用新属性 IsCompletedSuccessfully 或排除 IsFaultIsCancelled(如果您使用旧的 .NET fwk)。

【讨论】:

  • DoSomething() 中不会抛出异常,我已经尝试过这种方式。我无法编辑 DoSomething() 的源代码来引发异常。所以我不能用这种方式。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-06-14
相关资源
最近更新 更多