简体   繁体   中英

Push async method's execution to thread pool thread

Consider this Windows Forms code (one could write similar WPF analogue):

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    private void TraceThreadInfo([CallerMemberName]string callerName = null)
    {
        Trace.WriteLine($"{callerName} is running on UI thread: {!this.InvokeRequired}");
    }

    private void DoCpuBoundWork([CallerMemberName]string callerName = null)
    {
        TraceThreadInfo(callerName);
        for (var i = 0; i < 1000000000; i++)
        {
            // do some work here
        }
    }

    private async Task Foo()
    {
        DoCpuBoundWork();
        await Bar();
    }

    private async Task Bar()
    {
        DoCpuBoundWork();
        await Boo();
    }

    private async Task Boo()
    {
        DoCpuBoundWork();
        // e.g., saving changes to database
        await Task.Delay(1000);
    }

    private async void button1_Click(object sender, EventArgs e)
    {
        TraceThreadInfo();
        await Foo();
        Trace.WriteLine("Complete.");
        TraceThreadInfo();
    }
}

Here's the chain of Foo / Bar / Boo methods, which I want to execute asynchronously, without blocking of UI thread. These methods are similar, in sense that all of them makes some CPU-bound work and ultimately calls "true" asynchronous operation (eg, perform some heavy calculations an save result to the database).

The output from the code above is this:

button1_Click is running on UI thread: True
Foo is running on UI thread: True
Bar is running on UI thread: True
Boo is running on UI thread: True
Complete.
button1_Click is running on UI thread: True

So, all this stuff executes synchronously.
I know about capturing of current context by built-in awaitables. So, I thought, that it will be enough to call ConfigureAwait(false) like this:

    private async Task Foo()
    {
        await Task.Delay(0).ConfigureAwait(false);
        DoCpuBoundWork();
        await Bar();
    }

but, actually, this doesn't change anything.
I'm wondering, how this can be "pushed" to thread pool thread, assuming, that at the end of button1_Click method I need to return to UI thread?

Edit .

Task.Delay(0) actually optimizes call, when its argument is 0 (thanks to @usr for the note). This:

    private async Task Foo()
    {
        await Task.Delay(1).ConfigureAwait(false);
        DoCpuBoundWork();
        await Bar();
    }

will works, as expected (everything executes on thread pool, except button1_Click 's code). But this is even worse: to capture context or to not capture depends on awaitable implementation.

Your await Task.Delay(0).ConfigureAwait(false); is a poor mans Task.Yield() attempt (which does not work because I guess that Delay optimizes itself out if the argument is zero).

I would not recommend yield here.

I think in your click handler you should push to the thread-pool:

await Task.Run(async () => await Foo());

This is very simple and always works if the method you are calling does not depend on the synchronization context of the UI. This is architecturally nice because non of the methods you are calling need or should be aware by what code they are called.

Task.Factory.StartNew() can be used to easily run code in a separate thread. You can optionally make the current thread wait for the second thread by calling Wait().

Below is a quick sample program which demonstrates the relevant pieces:

class Program
{
    static void Main(string[] args)
    {
        Task t = Task.Factory.StartNew(() =>
        {
            //New thread starts here.
            LongRunningThread();
        });
        Console.WriteLine("Thread started");
        t.Wait(); //Wait for thread to complete (optional)
        Console.WriteLine("Thread complete");
        Console.ReadKey();
    }

    static void LongRunningThread()
    {
        Console.WriteLine("Doing work");
        //do work here
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM