任务。什么时候没完成

时间:2018-01-18 16:38:16

标签: c# multithreading task task-parallel-library

我是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 个答案:

答案 0 :(得分:2)

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

  • ContinueWith也会返回一个任务(然后放在taskList
  • 您已声明该任务只应在前一个任务出现故障时运行(由于TaskContinuationOptions.OnlyOnFaulted
  • 您提供的示例未进入故障状态。
  • 由于这些选项而未运行ContinueWith任务,因此enters the state Cancelled
  • 等待取消的任务会引发异常
  • 在您的Main方法中,Task.Run(() => Unsubscribe())调用的返回值不是await,因此永远不会被注意到。

继续:

  • 您不应在任务上使用Wait()。您已经开始使用asyncawait,通过您的计划使用它。 真正使用Wait()的唯一原因是你当然不能使用异步。尽量避免它。

  • Asyncasync时,请始终使用后缀multiDexEnabled = true 命名。这显示了使用该方法的用户的明确意图。

答案 1 :(得分:1)

我测试了这个。我在 var labelText = $("#label1").text(); if (!labelText) { $("#div1").show(); } 处收到异常,说任务已取消。这使得所有这一切都有意义:

  1. 根据默认答案指出,您已将WhenAll任务添加到ContinueWith
  2. taskList任务被取消,因为它应该运行ContinueWith,并且没有发生异常。
  3. 由于抛出(并且未处理)异常,因此OnlyOnFaulted的其余部分未运行。
  4. 您可以采取以下措施来改善这一点:

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

      Unsubscribe

    2. taskList.Add(SendUnsubscribe(chanID));方法中使用try / catch,而不是在任务中使用SendUnsubscribe

    3. 等待ContinueWith而非使用WhenAll

      Wait()

    4. 如果您认为await Task.WhenAll(taskList);无法处理任何例外情况,您始终可以将await Task.WhenAll(taskList);包裹在try / catch区块中。