Вам нужно изменить тип возвращаемого значения вашего метода Repopulate
, чтобы он возвращал задачу, представляющую асинхронную операцию.
Кроме того, вам не следует выполнять асинхронные операции из конструктора формы, поскольку вызов Task.Wait
приведет к блокировке вашего потока пользовательского интерфейса (и ваше приложение не будет отвечать на запросы). Вместо этого подпишитесь на его метод Form.Load
и выполняйте там асинхронные операции, используя ключевое слово await
, чтобы обработчик событий оставался асинхронным. Если вы не хотите, чтобы пользователь взаимодействовал с формой до завершения асинхронной операции, отключите форму в конструкторе и снова включите ее в конце обработчика Load
.
private async void Form1_Load(object sender, EventArgs e)
{
Task t = Repopulate();
// If you want to run its synchronous part on the thread pool:
// Task t = Task.Run(() => Repopulate());
// some other work here
await t;
}
async Task Repopulate()
{
var query = ParseObject.GetQuery("Offer");
IEnumerable<ParseObject> results = await query.FindAsync();
if (TitleList == null)
TitleList = new List<string>();
foreach (ParseObject po in results)
TitleList.Add(po.Get<string>("title"));
}
Обновление: для будущих читателей я перерабатываю свои комментарии в ответ:
Task.Wait
вызывает блокировку вызывающего потока до завершения задачи. Конструкторы форм и обработчики событий по своей природе работают в потоке пользовательского интерфейса, поэтому вызов Wait
внутри них приведет к блокировке потока пользовательского интерфейса. Ключевое слово await
, с другой стороны, приведет к тому, что текущий метод передаст управление обратно вызывающей стороне — в случае обработчиков событий это позволит потоку пользовательского интерфейса продолжить обработку событий. Ожидающий метод (обработчик событий) затем возобновит выполнение в потоке пользовательского интерфейса после завершения задачи.
Task.Wait
всегда будет блокировать вызывающий поток, независимо от того, вызывается ли он из конструктора или обработчика событий, поэтому его следует избегать, особенно при работе в потоке пользовательского интерфейса. С этой целью в C# 5 были введены ключевые слова async
и await
; однако они поддерживаются только в методах, а не в конструкторах. Это ограничение лежит в основе основной причины, по которой вам необходимо перенести код инициализации из конструктора формы в обработчик асинхронного события.
Что касается причины преждевременного возврата Task.Wait()
: в исходном коде задача t
представляет собой выполнение Task
, экземпляр которого вы создали в конструкторе формы. Эта задача выполняется Repopulate
; однако указанный метод вернется, как только обнаружит первый оператор await
, и выполнит остальную часть своей логики в режиме «запустил и забыл». В этом заключается опасность использования async void
— вы не узнаете, когда асинхронный метод завершит выполнение. (По этой причине async void
следует использовать только для обработчиков событий.) Другими словами, t.Wait()
возвращает значение, как только Repopulate
достигает своего первого await
.
Изменив сигнатуру Repopulate
на async Task
, вы получите еще одну задачу, представляющую завершение ее асинхронного выполнения, включая асинхронный вызов query.FindAsync()
и последующую за ним обработку. Когда Task.Run
передается асинхронная операция (Func<Task>
) в качестве аргумента, возвращаемая задача будет ожидать (развернуть) внутреннюю задачу. Вот почему следует использовать Task.Run
вместо Task.Start
или Task.Factory.StartNew
.
person
Douglas
schedule
24.03.2016