简体   繁体   English

为什么Task甚至在等待时完成

[英]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 . 我只想从新启动的任务MainTask获取Exception。 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? 即使在MainTaskawait命令,我也不知道为什么MainTask结束? 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 不理解异步委托,所以在这种情况下你需要使用Task.Run ,异常应该流过。

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. 它忽略了MainTask返回的任务,只是吞下了异常。

See this blog post for more details. 有关详细信息,请参阅此博客文章

Try using Task.Run instead and you'll get your exception: 尝试使用Task.Run ,您将获得异常:

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. 这里有很好的答案,但我想指出一个显而易见的事实 - Task.Factory.StartNew是完全冗余的,不必要的并且使用错误。

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. 此代码仅在延迟后为代码使用线程池线程,并在task.Wait()调用上重新task.Wait()异常 - 当然,您可能还想做其他事情。

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 :) 作为旁注,即使您不想明确等待任务完成,也不应该使用while (true) {}来阻止应用程序终止 - 一个简单的Console.ReadLine()将起作用同样,也不会将你的一个CPU核心推向100%利用率:)

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. 您应该使用TaskContinuationOptions.NotOnFaulted ,这意味着只有父任务没有任何异常时才会执行continue with task。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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