首页 > 解决方案 > WinForms 异步加载任务未完成

问题描述

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

在我最初的问题中,处理程序过早结束Form_Load(),所以我决定将Form_Load()处理程序中的所有内容包装在 aTask中,然后在Form_Show()处理程序中(在. 我关心的数据是,即将出来的。 Form_Load()TaskstringArraynull

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

我知道在这里挣扎,可能错过了一些基本概念。我觉得如果我能理解为什么stringArraynull,我就会明白为什么我的原作Form_Load()提前结束了。的财产formLoadTask怎么可能是这样?StatusRanToCompletionstringArraynull

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

谢谢

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);
    }
}

标签: c#winformsasynchronous

解决方案


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();
});

推荐阅读