为什么Task.WhenAll()阻止在这里?

时间:2014-12-09 21:27:27

标签: c# async-await

我有一个相当简单的代码片段,但它似乎阻止了Task.WhenAll()。 Main函数只调用new Test()。有人可以帮助找到根本原因吗?

internal class Test
{
    public static async Task<int> Foo()
    {
        return 0;
    }

    static Test()
    {
        var taskList = new List<Task>();

        for (int i = 0; i < 100; i++)
            taskList.Add(Task.Run(() => Foo()));

        Task.WhenAll(taskList.ToArray()).GetAwaiter().GetResult();
    }
}

2 个答案:

答案 0 :(得分:9)

CLR在运行静态构造函数时采用全局锁定。静态构造函数必须在其他线程可以在类上输入任何方法之前释放此锁。这是为了保证静态构造函数的运行,并且每个appdomain只完成一次。以下是线程更明确的问题。

class Test
{
    static Test() {
       var thread = new Thread(ThreadMethod);
       thread.Start();
       thread.Join();
    }

    private static void ThreadMethod()
    {
    }
}

在静态构造函数退出之前,新线程无法进入ThreadMethod,但静态构造函数无法退出,直到线程结束,即死锁。

您的示例只是一个更复杂的版本,它使用Task.Run而不是创建Thread并启动它然后GetResult阻止Thread.Join阻止

答案 1 :(得分:3)

你正陷入困境。请注意,您的代码位于类的静态构造函数中。这意味着在类型初始化程序完成之前,不会发生其他类型的初始化。但是在所有这些任务完成之前,您的类型初始化程序将无法完成但如果这些任务依赖于尚未初始化的其他类型,则无法完成。

解决方案是实现您尝试通过其他机制进行的初始化。如果需要,可以在静态构造函数中启动任务,但修复代码,以便实例可以在静态构造函数之外等待结果。例如,使用Lazy<T>

而不是使用静态构造函数
private static readonly Lazy<int> _tasksResult = new Lazy<int>(
    () => InitTest());

static int InitTest()
{
    var taskList = new List<Task>();

    for (int i = 0; i < 100; i++)
        taskList.Add(Task.Run(() => Foo()));

    return Task.WhenAll(taskList.ToArray()).GetAwaiter().GetResult();
}

或类似的东西(你真的不清楚你真正感兴趣的是什么结果,所以上面有一点挥手)。