在静态构造函数中调用时,用 Task.Run( ... ) 包装方法会挂起

Wrapping a method with Task.Run( ... ) hangs when called in a Static constructor

我有一系列long-运行ning函数。我想将它们包装在 Task 中,这样我就可以同时 运行 所有它们,而不是等待每个按顺序完成。

方法调用以及所有相关的字段值、方法和属性都存在于静态 class 中。

我遇到一个问题,其中静态 class 构造函数无法完成,因为当我用 Task.Run.

包装方法时它挂起

符合必要的 Mininmal, Complete and Verifiable 示例要求...

using System;
using System.Linq;
using System.Threading.Tasks;

namespace MCVEAsyncHang
{
    class Program
    {
        private static readonly string[] _foo;

        static Program()
        {
            _foo = Task.WhenAll(Task.Run(new Func<string>(One))).Result;
        }

        private static string One()
        {
            return "Foo";
        }

        private static void Print()
        {
            Console.WriteLine(
                _foo.Aggregate((total, current) => 
                     total + string.Format("{0}{1}", Environment.NewLine, current)));
        }

        static void Main(string[] args)
        {
            Print();
            Console.WriteLine("Done");
            Console.ReadLine();
        }
    }
}

我知道我可以创建一些其他方法并调用它(如果必须的话我会这样做(不情愿地这样))但如果可能的话,我宁愿将其保留在静态 class构造函数。

您的任务将在另一个线程中 运行,需要调用 _one。在您的 Program 类型初始化之前,该方法无法执行。

任务的线程将看到 Program 类型 已经 在主线程中初始化,因此将阻塞直到该线程完成类型的初始化。不幸的是,这不会发生——因为类型初始化器将阻塞直到你的任务完成。死锁。

基本上,您应该避免在静态构造函数中做太多工作。启动任务绝对感觉工作量太大。在这种情况下,死锁是显而易见的,但在其他情况下,它可能要微妙得多。 (在此之前,我已经花了 小时 调试类型初始化循环,这真的非常不好玩。那是单线程代码——我不敢想象它在多线程中会有多痛苦-线程环境。)