Почему ожидание холодной задачи не бросает
Я просто экспериментировал, чтобы увидеть, что произойдет, когда ждет холодная задача (т.е. Task
, которая не была запущена). К моему удивлению, код, который всегда висел навсегда, и "Finsihed" никогда не печатается. Я бы ожидал, что будет сделано исключение.
public async Task Test1()
{
var task = new Task(() => Thread.Sleep(1000));
//task.Start();
await task;
}
void Main()
{
Test1().Wait();
Console.WriteLine("Finished");
}
Тогда, хотя, возможно, задача может быть запущена из другого потока, поэтому я изменил код на:
public async Task Test1()
{
var task = new Task(() => Thread.Sleep(1000));
//task.Start();
await task;
Console.WriteLine("Test1 Finished");
}
void Main()
{
var task1 = Test1();
Task.Run(() =>
{
Task.Delay(5000);
task1.Start();
});
task1.Wait();
Console.WriteLine("Finished");
}
Но он все еще заблокирован в task1.Wait()
. Кто-нибудь знает, есть ли способ начать холодную задачу после ее ожидания?
В противном случае кажется, что нет возможности await
холодной задачи, поэтому, возможно, задача должна быть запущена в ожидании или исключение должно быть исключено.
Обновление
Я ждал неправильной задачи, т.е. внешней задачи, возвращаемой Test1
, а не той, которая была введена внутри него. Исключение InvalidOperationException, упомянутое @Jon Skeet, было брошено внутри Task.Run
, однако, поскольку результирующая задача не наблюдалась, исключение не было выбрано в основном потоке. Ввод try/catch
внутри Task.Run
или вызов Wait()
или Result
в задаче, возвращаемой Task.Run
, вызвал исключение в потоке основной консоли.
Ответы
Ответ 1
Вы пытаетесь запустить задачу, возвращенную методом async - это не холодная задача, с которой вы начали. Действительно, если вы добавите некоторую диагностику в ваш вызов Task.Run
, вы увидите, что генерируется исключение:
System.InvalidOperationException: запуск не может быть вызван задачей с обещанием.
Вот пример, показывающий, что я думаю, что вы действительно пытались сделать:
using System;
using System.Threading;
using System.Threading.Tasks;
public class Test
{
static void Main(string[] args)
{
// Not using Task.Delay! That would be pointless
Task t1 = new Task(() => Thread.Sleep(1000));
Task t2 = Await(t1);
Console.WriteLine(t2.Status);
Console.WriteLine("Starting original task");
t1.Start();
Console.WriteLine(t2.Status);
t2.Wait();
Console.WriteLine(t2.Status);
}
static async Task Await(Task task)
{
Console.WriteLine("Beginning awaiting");
await task;
Console.WriteLine("Finished awaiting");
}
}
Обратите внимание на использование Thread.Sleep
вместо Task.Delay
; если вы не используете результат Task.Delay
, он ничего не делает. Использование Thread.Sleep
- эмуляция реальной работы, выполняемой в другой задаче.
Что касается того, почему ожидание нераспределенной задачи не вызывает исключения - я считаю это разумным, если честно. Это позволяет использовать ситуации, подобные приведенным выше, что может в некоторых случаях облегчить жизнь. (Например, вы можете создать множество задач, прежде чем запускать их, и, возможно, вы захотите начать их до завершения.)
Ответ 2
Кто-нибудь знает, есть ли способ начать холодную задачу после того, как она Ожидается?
Вы все еще можете создать холодную задачу из метода async
и запустить ее позже, если это вам нужно:
class Program
{
public static async Task Test1()
{
await Task.Delay(1000);
Console.WriteLine("Test1 is about to finish");
}
static void Main(string[] args)
{
var taskOuter = new Task<Task>(Test1);
var taskInner = taskOuter.Unwrap();
Task.Run(() =>
{
Thread.Sleep(2000);
// run synchronously
taskOuter.RunSynchronously();
// or schedule
// taskOuter.Start(TaskScheduler.Defaut);
});
taskInner.Wait();
Console.WriteLine("Enter to exit");
Console.ReadLine();
}
}