#c# #asynchronous #async-await
#c# #асинхронный #async-await
Вопрос:
Я просто экспериментировал, чтобы посмотреть, что происходит, когда ожидается холодная задача (то есть, 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()
or Result
для задачи, возвращаемой, вызвал Task.Run
исключение в главном потоке консоли.
Ответ №1:
Вы пытаетесь запустить задачу, возвращенную методом async — это не та «холодная» задача, с которой вы начинали. Действительно, если вы добавите некоторую диагностику к своему Task.Run
вызову, вы увидите, что генерируется исключение:
Система.Исключение InvalidOperationException: Start не может быть вызван для задачи в стиле promise.
Вот пример, показывающий, что, я думаю, вы действительно пытались сделать:
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
— это эмуляция реальной работы, которая должна быть выполнена в другой задаче.
Что касается того, почему ожидание незавершенной задачи не генерирует исключение — я думаю, что это разумно, если честно. Это позволяет допустимым ситуациям, подобным приведенным выше, что в некоторых случаях может облегчить жизнь. (Например, вы можете создать много задач перед их запуском, и вы можете захотеть начать ждать их завершения, прежде чем запускать их.)
Комментарии:
1. Спасибо, Джон, я думаю, я немного запутался
Ответ №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();
}
}