WinForms 异步加载任务未完成

WinForms Async Load Task Does Not Complete

我正在使用 .NET Framework v4.7.2。我在下面有原始代码的简化版本。

在我最初的问题中,Form_Load() 处理程序过早结束,所以我决定将 Form_Load() 处理程序中的所有内容包装在 Task 中,然后在 Form_Show()处理程序(在 Form_Load() 之后触发 )提取我在 Task 中异步加载的数据。我关心的数据是 stringArray 出来的 null.

显然,我更愿意弄清楚 为什么 Form_Load() 提前结束,但我没有运气。

我知道我在这里苦苦挣扎,可能遗漏了一些基本概念。我觉得如果我能理解为什么stringArraynull,我就会理解为什么我原来的Form_Load()提前结束了。 formLoadTaskStatus属性是RanToCompletion那么stringArray怎么会是null呢?

请记住这是真实代码的简化版本。

谢谢

interface IRetriever
{
    Task<String[]> GetStringArray();
}

class Retriever : IRetriever
{
    public async Task<string[]> GetStringArray()
    {
        return await Task.FromResult(new string[] { "More", "Larry", "Curly", });
    }
}

static class Director
{
    internal static async Task<IRetriever> CreateRetriever()
    {
        return await Task<IRetriever>.Factory.StartNew(() =>
        {
            // Simulate some I/O bound waiting...
            Thread.Sleep(5000);
            return new Retriever();
        });
    }
}

public partial class Form1 : Form
{
    private static String[] stringArray;
    private static IRetriever retriever;
    internal Task formLoadTask;

    public Form1()
    {
        InitializeComponent();
    }

    private void Form1_Load(object sender, EventArgs e)
    {
        formLoadTask = Task.Factory.StartNew(async () =>
        {
            retriever = await Director.CreateRetriever();
            stringArray = await retriever.GetStringArray();

        });
    }

    private void Form1_Shown(object sender, EventArgs e)
    {
        // I know I am tying up the UI thread here. This is just an example.
        formLoadTask.Wait();

        // At this point the formLoadTask is finished, so shouldn't the stringArray be populated?

        // The next line throws a NullReferenceException because stringArray is null.
        // Why??
        System.Diagnostics.Debug.WriteLine(stringArray.Length);
    }
}

StartNew 无法识别您的异步委托。它把它当作一个没有人最终等待的 Func<Task>。您存储在 formLoadTask 中的任务只是启动任务的任务,而不是任务本身。

您可以解包任务或使用更现代的 Task.Run,它可以正确处理异步委托。

formLoadTask = Task.Factory.StartNew(async () =>
{
    retriever = await Director.CreateRetriever();
    stringArray = await retriever.GetStringArray();
}).Unwrap();

或(首选):

formLoadTask = Task.Run(async () =>
{
    retriever = await Director.CreateRetriever();
    stringArray = await retriever.GetStringArray();
});