簡體   English   中英

為什么Task甚至在等待時完成

[英]Why Task finishes even in await

我在以下代碼中遇到問題:

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!!");
}

我只想從新啟動的任務MainTask獲取Exception。 但結果不是我的預期。

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

正如您可以看到結果,任務在“等待結束!!”之前完成 控制台日志。 即使在MainTaskawait命令,我也不知道為什么MainTask結束? 我錯過了什么嗎?

Task.Factory.StartNew 不理解異步委托,所以在這種情況下你需要使用Task.Run ,異常應該流過。

Task.Factory.StartNew(MainTask);

基本上相當於

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

它忽略了MainTask返回的任務,只是吞下了異常。

有關詳細信息,請參閱此博客文章

嘗試使用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!!");
}

這里有很好的答案,但我想指出一個顯而易見的事實 - Task.Factory.StartNew是完全冗余的,不必要的並且使用錯誤。

如果你更換

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

Task newTask = MainTask();

您將獲得您期望的行為,而不會浪費另一個線程池線程來啟動另一個線程池線程。 事實上,如果您想將您的示例重寫為更慣用,您可以使用以下內容:

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!!");
}

此代碼僅在延遲后為代碼使用線程池線程,並在task.Wait()調用上重新task.Wait()異常 - 當然,您可能還想做其他事情。

作為旁注,即使您不想明確等待任務完成,也不應該使用while (true) {}來阻止應用程序終止 - 一個簡單的Console.ReadLine()將起作用同樣,也不會將你的一個CPU核心推向100%利用率:)

我在這里修改了你的問題以捕獲異常。

    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!!");


    }

您應該使用TaskContinuationOptions.NotOnFaulted ,這意味着只有父任務沒有任何異常時才會執行continue with task。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM