简体   繁体   中英

Why Task finishes even in await

I have a problem in the following code:

static void Main (string[] args)
{
    Task newTask = Task.Factory.StartNew(MainTask);
    newTask.ContinueWith ((Task someTask) => 
    {
        Console.WriteLine ("Main State=" + someTask.Status.ToString () + " IsFaulted=" + someTask.IsFaulted+" isComplete="+someTask.IsCompleted);
    });
    while (true) 
    {

    }
}

static async Task MainTask()
{
    Console.WriteLine ("MainStarted!");
    Task someTask = Task.Factory.StartNew (() => 
    {
        Console.WriteLine ("SleepStarted!");
        Thread.Sleep(1000);
        Console.WriteLine ("SleepEnded!");
    });
    await someTask;
    Console.WriteLine ("Waiting Ended!!");
    throw new Exception ("CustomException!");
    Console.WriteLine ("NeverReaches here!!");
}

I just want to get Exception from new started task MainTask . But the result was not what I was expected.

MainStarted!
Main State = RanToCompletion IsFaulted = False isComplete = True
SleepStarted!
SleepEnded!
Waiting Ended!!

As you can see the result, task finishes before "Waiting Ended!!" console log. I don't have a clue that why MainTask ended even if in MainTask has await command inside? Did I missed something?

Task.Factory.StartNew does not understand async delegates so you need to use Task.Run in this case and the exception should flow through.

Task.Factory.StartNew(MainTask);

is essentially equivalent to

Task.Factory.StartNew(() => MainTask);

which ignores the returned task from MainTask and the exception just gets swallowed.

See this blog post for more details.

Try using Task.Run instead and you'll get your exception:

void Main(string[] args)
{
    Task newTask = Task.Run(MainTask);
    newTask.ContinueWith((Task someTask) =>
   {
       Console.WriteLine("Main State=" + someTask.Status.ToString() + " IsFaulted=" + someTask.IsFaulted + " isComplete=" + someTask.IsCompleted);
   });
    while (true)
    {

    }
}

static async Task MainTask()
{
    Console.WriteLine("MainStarted!");
    Task someTask = Task.Run(() =>
   {
       Console.WriteLine("SleepStarted!");
       Thread.Sleep(1000);
       Console.WriteLine("SleepEnded!");
   });
    await someTask;
    Console.WriteLine("Waiting Ended!!");
    throw new Exception("CustomException!");
    Console.WriteLine("NeverReaches here!!");
}

There's great answers here, but I'd like to point out the obvious - the Task.Factory.StartNew is completely redundant, unnecessary and used wrong.

If you replace

Task newTask = Task.Factory.StartNew(MainTask);

with

Task newTask = MainTask();

You'll get exactly the behaviour you expect, without wasting yet another threadpool thread just to start another threadpool thread. In fact, if you wanted to rewrite your example to be more idiomatic, you'd use something like this:

static void Main (string[] args)
{
    var task = 
      MainTask()
      .ContinueWith(t => Console.WriteLine("Main State={0}", t.Status));

    task.Wait();
}

static async Task MainTask()
{
    Console.WriteLine ("MainStarted!");

    await Task.Delay(1000);

    Console.WriteLine ("Waiting Ended!!");

    throw new Exception ("CustomException!");

    Console.WriteLine ("NeverReaches here!!");
}

This code only uses a thread pool thread for the code after the delay, and rethrows the exception on the task.Wait() call - you might want to do something else, of course.

As a side-note, even if you don't want to explicitly wait for the task to complete, you shouldn't use while (true) {} to prevent the application from terminating - a simple Console.ReadLine() will work just as well, and isn't going to push one of your CPU cores to 100% utilization :)

I modified your problem here to catch the exceptions.

    static void Main(string[] args)
    {
        DoFoo();
        Console.ReadKey();
    }



    static async void DoFoo()
    {
        try
        {
            await Foo();
        }
        catch (Exception ex)
        {
            //This is where you can catch your exception
        }
    }




    static async Task Foo()
    {

        await MainTask().ContinueWith((Task someTask) =>
        {

            Console.WriteLine("Main State=" + someTask.Status.ToString() + " IsFaulted=" + someTask.IsFaulted + " isComplete=" + someTask.IsCompleted);

        }, TaskContinuationOptions.NotOnFaulted);

    }

    static async Task MainTask()
    {


        Console.WriteLine("MainStarted!");
        Task someTask = Task.Run(() =>
        {
            Console.WriteLine("SleepStarted!");
            Thread.Sleep(1000);
            Console.WriteLine("SleepEnded!");
        });
        await someTask;
        throw new Exception("CustomException!");

        Console.WriteLine("Waiting Ended!!");


    }

you should use TaskContinuationOptions.NotOnFaulted which means that the continue with task will only execute if the parent task did not had any exceptions.

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