代码之家  ›  专栏  ›  技术社区  ›  altwood

任务未完成时[复制]

  •  1
  • altwood  · 技术社区  · 7 年前

    我不熟悉C语言中的任务,遇到了一个我不理解的问题。在下面的代码中,有人可以解释为什么Unsubscribe方法末尾的WriteLine从未被调用。它似乎与前面foreach循环中的ContinueWith相关,好像我评论说它工作得很好。 谢谢

    using System;
    using System.Threading;
    using System.Linq;
    using System.Threading.Tasks;
    using System.Collections.Generic;
    
    public class Example
    {
        public static void Main()
        {
    
            Task.Run(() => Unsubscribe());
    
            Console.WriteLine("Method: Main. End");
    
            Console.ReadKey();
    
        }
    
        private static void Unsubscribe()
        {
            Dictionary<int, string> dicPairsBySubID = new Dictionary<int, string>();
    
            dicPairsBySubID.Add(1, "A");
            dicPairsBySubID.Add(2, "B");
            dicPairsBySubID.Add(3, "C");
            dicPairsBySubID.Add(4, "D");
            dicPairsBySubID.Add(5, "E");
    
            List<Task> taskList = new List<Task>();
    
            foreach (KeyValuePair<int, string> sub in dicPairsBySubID)
            {
                int chanID = sub.Key;
    
                Task task = Task.Run(() => SendUnsubscribe(chanID))
                .ContinueWith(tsk =>
                {
                    var flattened = tsk.Exception.Flatten();
    
                    flattened.Handle(ex =>
                    {
                        Console.WriteLine(string.Format("Error unsubscribing pair {0} (chanID = {2})", sub.Value, chanID), ex);
                        return true;
                    });
    
                }, TaskContinuationOptions.OnlyOnFaulted);
    
                taskList.Add(task);
            }
    
            Task.WhenAll(taskList).Wait();
    
            // WHY DOES THIS NEVER GET RUN?
            Console.WriteLine("Method: Unsubscribe. End");
    
    
        }
    
        private static async Task SendUnsubscribe(int chanID)
        {
    
    
            await SendAsync(chanID);
    
    
            Console.WriteLine("Method: SendUnsubscribe. Chan ID: " + chanID);
    
    
        }
    
        private static async Task SendAsync(int chanID)
        {
    
            await Task.Run(() => Thread.Sleep(1000));
            Console.WriteLine("Method: SendAsync. Chan ID: " + chanID);
    
        } 
    
    
    }
    
    2 回复  |  直到 7 年前
        1
  •  2
  •   default    7 年前

    您的任务引发了一个无法捕获的异常,因为:

    • ContinueWith 还返回一个任务(然后放置在 taskList )
    • 您已经声明,仅当前一个任务出现故障时(由于以下原因),才应运行该任务 TaskContinuationOptions.OnlyOnFaulted )
    • 您给出的示例未进入故障状态。
    • 当由于这些选项而未运行ContinueWith任务时 enters the state Cancelled
    • 等待已取消的任务引发异常
    • 在Main方法中 Task.Run(() => Unsubscribe()) 呼叫不是 await ed,因此它从未被注意到。

    进一步说明:

    • 您不应该使用 Wait() 关于任务。您已经开始使用 async 等候 ,通过您的程序使用它。 真正使用 等待() 就是您肯定不能使用异步的时候。一定要尽量避免。

    • 始终使用后缀命名方法 Async 当他们是 异步 . 这显示了使用该方法的用户的明确意图。

        2
  •  1
  •   Gabriel Luci    7 年前

    我测试了一下。我在 WhenAll 表示任务已取消。这一切都有意义:

    1. 正如Default的回答所指出的,您正在添加 ContinueWith 任务收件人 taskList .
    2. 这个 继续使用 任务被取消,因为它应该运行 OnlyOnFaulted ,没有发生异常。
    3. 由于该异常是抛出(而不是处理)的 Unsubscribe 未运行。

    以下是一些可以改进的方法:

    1. 不要使用任务。运行只是为了运行异步方法。您可以这样做:

      taskList.Add(SendUnsubscribe(chanID));

    2. 使用 try / catch 在您的 SendUnsubscribe 方法,而不是使用 继续使用 在任务上。

    3. 等候 惠纳尔 而不是使用 Wait() :

      await Task.WhenAll(taskList);

    您可以随时包装 等待任务。WhenAll(任务列表); 在一个 尝试 / 接住 如果您认为可能发生任何异常,请阻止 发送取消订阅 无法处理。