Вложен метод с async блокира без await

+7 гласа
142 прегледа
попитан 2016 април 6 от Viktor.Ivanov. (1,550 точки)
Не съм сигурен какво се случва. Доколкото разбирам, докато методът среща await трябва да си работи и да продължава като се изпълни изчакваната задача. Обаче не такова поведение виждам. Ето два прости метода:

public static async Task<bool> Level1()
{            
    var t = await Level2();     
    Console.WriteLine("L1 done.");
    return t;                    
}
public static async Task<bool> Level2()
{
    int delay = 10;
    Thread.Sleep(100);
    var t = new TaskFactory();
    t.StartNew(() => { Thread.Sleep(delay); });
    Console.WriteLine("L2 done.");
    return true;
}

В Main метода правя следното

static void Main(string[] args)

{
    Level1();
    Console.WriteLine("Main done.");
}

Очаквам Main() да продължи след извикването на Level1(), тъй като не го изчаква и защото в    Level1() има await. Резултатът обаче е следният:

L2 done.
L1 done.
Main done.

Това показва,че методите ми работят синхронно. Като поправя Level2() да използва await проблемът се оправя и извикването на Level1() не блокира. Защо трябва да ползвам await във вложения метод Level2(), за да не дава на Level1() да блокира Main()?

1 отговор

+1 глас
отговорени 2016 април 7 от valeri.hristov (7,340 точки)
избран 2016 април 11 от Mitko Vasilev
 
Най-добър отговор

Като маркираш метод с async това не го прави асинхронен. Само му позволява да използва await(и вкарва резултата или exception-а с Task).

Ако имаш метод с async, който не изчаква никакви асинхронни операции в него, то той е напълно синхронен и ще се изпълни от нишката,която го извиква.

Това,което пропускаш, най-вероятно е, че не използването на await кара метода да работи асинхронно, а използването на await върху незавършени Task-ове. Когато Task-ът е завършен,няма причина да спираш метода. Просто можеш да си вземеш резултата и да продължиш синхронно.

В твоя случай Level2 работи синхронно. Когато Level1 е готов да изчаква задачата си, тя вече е приключила и затова Level2 продължава синхронно. Не забравяй, че изчакваш Task,а не метод, и кодът ти всъщнос работи така:

public static async Task<bool> Level1()

{           

    var $task = Level2(); // Върви синхронно

    var t = await $task; // изчаква завършен Task     

    Console.WriteLine("L1 done."); 

    return t;                    

}

коментиран 2016 април 8 от Nikola.Nikolov. (3,100 точки)
Благодаря за отговора, помогна ми в работата ;)
...