简体   繁体   中英

What causes a Task to complete?

I'm trying to find out how to use WhenAll to let two methods run at once, and once they both finish, collect the results without blocking by using .Result

I have this little console app test:

using System.Diagnostics;
using System.Threading.Tasks;

namespace ConsoleApplication2
{
    class Program
    {
        public static void Main(string[] args)
        {
            var run = TaskRunner();
            Debug.WriteLine(run);
            if (run.IsCompleted)
            {
                Debug.WriteLine("this worked!");
            } else
            {
                Debug.WriteLine("this failed!");
            }
        }

        public static async Task<string> TaskRunner()
        {
            var taskOne = OneAsync();
            var taskTwo = TwoAsync();
            var tasks = await Task.WhenAll(taskOne, taskTwo);
            var retval = tasks[0] + tasks[1];
            return retval;
        }

        public static Task<string> OneAsync()
        {
            return Task.Run(() =>
            {
                return "test1";

            });
        }

        public static Task<string> TwoAsync()
        {
            return Task.Run(() =>
            {
                return "test2";

            });
        }
    }
}

This currently prints this worked! to my Output window... However, if I comment out Debug.WriteLine(run); it prints this failed! ... Why does the Task complete simply by being logged to the output window?

I'm trying to understand a huge problem in a complex piece of code and this little test is my MCVE to hopefully shed some light on what is happening behind the scenes.

This happens just by pure chance. The way you are starting your task is with Task.Run . This essentially creates a new thread on which the (synchronous) action is executed. It returns a task for the completion of that thread.

So OneAsync and TwoAsync will each spawn a new thread that then immediately returns a string. This will happen very quickly but there's still some overhead for creating those threads which means that it won't be instantaneous .

TaskRunner then calls both those methods (spawning the threads), and then asynchronously waits for both threads to finish. Since the threads are not completely instantly, this TaskRunner method also won't complete instantly.

Now, in your main, you are starting the asynchronous TaskRunner , which we figured will take “a very short moment” . You do not await the task, so the execution continues immediately. Debug.WriteLine is executed to print something (it probably doesn't really matter that it's the task in question that is being printed), and then you are checking the state of the task.

Since printing stuff is relatively slow (compared to other operations), this is probably the reason why the tasks ends up being completed. And when you remove the printing, the if is just reached too quickly for the task to finish.

As you likely noticed, working like that with asynchronous tasks does not appear to be a good idea. That's why you should always await the task when you depend on its result.

// note the *async* here; async main methods are supported with C# 7.1
public static async void Main(string[] args)
{
    var run = TaskRunner();

    // await the task
    await run;

    if (run.IsCompleted)
    {
        Debug.WriteLine("this worked!");
    }
    else
    {
        Debug.WriteLine("this failed!");
    }
}

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