Kixoka
Kixoka

Reputation: 1161

how to display/report back progress of Task(Async) actions?

I have the need to move some processes to async. I have 5 methods that I need to call individually and have run in the background so the user can continue on with their work.

The test code below seems to work... but I haven't been able to figure out how to return information (message) indicating that the a task has completed. The class will be called from a separate windows form so that the progress can be displayed....

from the form:

    async void BtnGo_Click(object sender, System.EventArgs e)
    {
        label2.Text = @"Starting tasks...";
        var progress = new Progress<string>(
            p =>
            {
                label2.Text = p;
            });
        await TestTask.MyTestMain(progress);
    }

the class:

public static  class TestTask
{
    public static Task MyTestMain(IProgress<string> pProgress)
    {
        return SomethingAsync(pProgress);
    }

    private static async Task SomethingAsync(IProgress<string> pProgress)
    {
        var t1 = SomeThing1(pProgress);
        var t2 = SomeThing2(pProgress);

        await Task.WhenAll(t1, t2);

        if (pProgress != null) pProgress.Report(@"all tasks completed");
    }

    private static async Task SomeThing1()
    {
        await Task.Delay(9000);

        var filename = @"c:\temp\tt1.txt";

        if (File.Exists(filename))
            File.Delete(filename);

        using (TextWriter tw = new StreamWriter(filename))
        {
            await tw.WriteLineAsync(DateTime.Now.ToLongDateString());
        }
        if (pProgress != null) pProgress.Report(@"t1 completed");
    }

    private static async Task SomeThing2()
    {
        await Task.Delay(7000);

        var filename = @"c:\temp\tt2.txt";

        if (File.Exists(filename))
            File.Delete(filename);

        using (TextWriter tw = new StreamWriter(filename))
        {
            await tw.WriteLineAsync(DateTime.Now.ToLongDateString());
        }
        if (pProgress != null) pProgress.Report(@"t2 completed");
    }
}

I would like know when each task has completed. Any help or direction would be appreciated.

EDIT I have edited this post to reflect my changes... I still cannot get a progress report back to the UI... any thoughts?

Upvotes: 2

Views: 997

Answers (1)

Yuval Itzchakov
Yuval Itzchakov

Reputation: 149656

You're doing IO bound work, you don't need to use thread-pool threads.

Transform your methods to use the async APIs of StreamWriter:

private static async Task FirstThingAsync()
{
    var filename = @"c:\temp\tt1.txt";

    if (File.Exists(filename))
        File.Delete(filename);

    using (TextWriter tw = new StreamWriter(filename))
    {
       await tw.WriteLineAsync(DateTime.Now);
    }
}

Same for your second method, and then you can asynchronously wait on them concurrently:

private static async Task SomethingAsync()
{
    var firstThing = FirstThingAsync();
    var secondThing = SecondThingAsync();

    await Task.WhenAll(firstThing, secondThing);
}

Edit:

You're never reaching your first Progress.Report call because your code is throwing an InvalidOperationException when you call t.Start() on a promise-style task:

t1.Start();
await t1;

t2.Start();
await t2;

The task returned from both method calls is a "hot task", meaning it's operation is already started. The docs on Task.Start say:

InvalidOperationException: The Task is not in a valid state to be started. It may have already been started, executed, or canceled, or it may have been created in a manner that doesn't support direct scheduling.

The reason you're not seeing that exception is because you're swallowing it:

var t = SomethingAsync(pProgress);

When you don't await on the async operation. Your method calls should look like this:

public static Task MyTestMain(IProgress<string> pProgress)
{
    return SomethingAsync(pProgress);
}

async void BtnGo_Click(object sender, System.EventArgs e)
{
    label2.Text = @"Starting tasks...";
    var progress = new Progress<string>(
        p =>
        {
            label2.Text = p;
        });
    await TestTask.MyTestMain(progress);
}

Upvotes: 1

Related Questions