Home > OS >  WinForms Async Load Task Does Not Complete
WinForms Async Load Task Does Not Complete

Time:11-02

I am using .NET Framework v4.7.2. I have a simplified version of the original code below.

In my original problem, the Form_Load() handler was ending prematurely so I decided to wrap everything from the Form_Load() handler inside a Task and then in the Form_Show() handler (which fires after Form_Load()) pull out the data I asynchronously loaded in the Task. The data I am concerned about is the stringArray, which is coming out null.

Obviously, I would have preferred to figure out why Form_Load() was ending prematurely but I had no luck in that.

I know am struggling here and probably missing some fundamental concept. I feel that if I can understand why stringArray is null, I will understand why my original Form_Load() ended prematurely. The formLoadTask's Status property is RanToCompletion so how could stringArray be null?

Please remember this is a simplified version of the real code.

Thanks

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

CodePudding user response:

StartNew doesn't recognize your async delegate. It is treating it as a Func<Task> that nobody ends up awaiting. The task that you're storing in formLoadTask is just the task that started the task, not the task itself.

You can either unwrap the task or use the more modern Task.Run, which handles async delegates properly.

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

or (preferred):

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