C# Task.Wheall未完成

C# Task.Wheall未完成,c#,multithreading,task,task-parallel-library,C#,Multithreading,Task,Task Parallel Library,我不熟悉C#中的任务,遇到了一个我不理解的问题。在下面的代码中,有人可以解释为什么Unsubscribe方法末尾的WriteLine从未被调用。它似乎与前面foreach循环中的ContinueWith有关,好像我评论说它工作得很好。 谢谢 使用系统; 使用系统线程; 使用System.Linq; 使用System.Threading.Tasks; 使用System.Collections.Generic; 公开课范例 { 公共静态void Main() { Task.Run(()=>Unsub

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

使用系统;
使用系统线程;
使用System.Linq;
使用System.Threading.Tasks;
使用System.Collections.Generic;
公开课范例
{
公共静态void Main()
{
Task.Run(()=>Unsubscribe());
Console.WriteLine(“方法:Main.End”);
Console.ReadKey();
}
私有静态无效取消订阅()
{
Dictionary dicPairsBySubID=新字典();
添加(1,“A”);
添加(2,“B”);
添加(3,“C”);
添加(4,“D”);
添加(5,“E”);
List taskList=新列表();
foreach(dicPairsBySubID中的KeyValuePair子对象)
{
int chanID=子密钥;
Task Task=Task.Run(()=>SendUnsubscribe(chanID))
.ContinueWith(tsk=>
{
var Flatten=tsk.Exception.Flatten();
展平.句柄(ex=>
{
WriteLine(string.Format(“错误取消订阅对{0}(chanID={2})”,sub.Value,chanID),ex);
返回true;
});
},TaskContinuationOptions.OnlyOnFaulted);
任务列表。添加(任务);
}
Task.WhenAll(taskList.Wait();
//为什么这个从来没有运行过?
Console.WriteLine(“方法:Unsubscribe.End”);
}
专用静态异步任务SendUnsubscribe(int chanID)
{
等待发送异步(chanID);
Console.WriteLine(“方法:SendUnsubscribe.Chan ID:+chanID”);
}
专用静态异步任务SendAsync(int chanID)
{
等待任务。运行(()=>Thread.Sleep(1000));
Console.WriteLine(“方法:SendAsync.Chan ID:+chanID”);
} 
}

我对此进行了测试。我在
收到一个异常,当时所有的
都说一个任务被取消了。这一切都是有道理的:

  • 正如Default的回答所指出的,您正在将
    ContinueWith
    任务添加到
    任务列表中
  • ContinueWith
    任务将被取消,因为它应该仅在出现故障的情况下运行
    ,并且没有发生异常
  • 由于引发(且未处理)异常,因此
    Unsubscribe
    的其余部分不会运行
  • 以下是一些可以改进的方法:

  • 不要使用Task.Run来运行异步方法。您可以这样做:

    taskList.Add(SendUnsubscribe(chanID))

  • SendUnsubscribe
    方法中使用
    try
    /
    catch
    ,而不是在任务中使用
    ContinueWith

  • 等待
    whalll
    而不是使用
    Wait()

    wait Task.WhenAll(任务列表)


  • 您可以始终包装
    wait Task.WhenAll(任务列表)发送取消订阅
    无法处理,请在
    中尝试
    /
    捕获
    块中的code>。

    您的任务会抛出一个未捕获的异常,因为:

    • ContinueWith
      还返回一个任务(然后将其放入
      任务列表中
    • 您已经声明,仅当上一个任务出现故障时才应运行该任务(由于
      TaskContinuationOptions.OnlyOnFaulted
    • 您给出的示例未进入故障状态
    • 当由于这些选项而未运行ContinueWith任务时,它将
    • 等待已取消的任务会引发异常
    • 在Main方法中,
      Task.Run(()=>Unsubscribe())
      调用的返回值不是
      wait
      ed,因此不会被注意到
    进一步说明:

    • 您不应该在任务上使用
      Wait()
      。您已经开始使用
      async
      wait
      ,请通过程序使用它。 真正使用
      Wait()。一定要尽量避免

    • 当方法是
      Async
      时,请始终使用后缀
      Async
      命名方法。这显示了使用该方法的用户的明确意图


    如果要使用异步,请正确使用。使用C#7.1和
    async main
    。不要使用
    .ContinueWith()
    .Wait()
    。那些是代码气味。您需要等待异步调用。仔细阅读。你不需要所有这些
    任务。运行
    s。特别是,当SendUnsubscribe已经是一个任务,甚至不等待它的结果或异常时,将其包装在一个中有什么意义<代码>任务.WhenAll(任务列表).Wait()也可以替换为
    等待任务.WhenAll(任务列表)专用异步任务Unsubscribe()
    执行code>。这与问题无关。将延续标记为
    OnlyOnFaulted
    并不意味着它永远不会完成如果先行项没有出现故障,这只意味着委托没有运行,任务也被标记为完成。问题并不是问为什么该延续中的代码没有运行,而是问为什么所有的
    都没有完成,而这与该延续是只在有故障的任务上运行,还是这些任务有故障无关。@谢谢您的输入!我在实际尝试代码后编辑了这个问题。现在应该更正确了(举一个例子)@Servy这个答案还有什么问题吗?谢谢你的默认,谢谢你的“进一步”,我很感激你的建议。我想我正在慢慢开始掌握任务和异步/等待的诀窍。
    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);
    
        } 
    
    
    }