Я создал класс Out<T>
, намереваясь использовать его в качестве параметра out
в асинхронных методах.
class Out<T>
{
public T Value { get; set; }
}
Вот пример того, как я собираюсь использовать его:
async Task DoWorkAsync(Out<int> arg)
{
arg.Value = 13;
await Task.Delay(500); // Placeholder for a really useful async operation
}
Value
всегда будет назначаться перед первым await
внутри асинхронного метода. Другими словами, назначение будет происходить в синхронной части метода. Таким образом, вызывающая сторона может получить значение сразу после создания Task
и использовать его до ожидания Task
:
var arg = new Out<int>();
var task = DoWorkAsync(arg);
Console.WriteLine(arg.Value); // Prints '13' in the console :-)
await task;
До этого момента все работает отлично. Проблема возникает, когда становится обязательным запуск метода DoWorkAsync
в потоке пула потоков. Сначала я попробовал самый простой подход с Task.Run
:
var arg = new Out<int>();
var task = Task.Run(() => DoAsync(arg));
Console.WriteLine(arg.Value); // Prints '0' in the console :-(
await task;
Это не сработало. Еще одна попытка:
var arg = new Out<int>();
var task = Task.Run(async () => await DoWorkAsync(arg));
Console.WriteLine(arg.Value); // Prints '0' in the console :-(
await task;
Не сработало. После долгих раздумий я понял, что текущий поток не может получить arg.Value
, пока поток пула потоков не обработает синхронную часть метода, поэтому текущий поток должен ждать. Затем, после получения arg.Value
, текущий поток должен еще раз подождать асинхронную часть метода. Это привело меня к решению проблемы:
var arg = new Out<int>();
var task = await Task.Factory.StartNew(() => DoWorkAsync(arg),
CancellationToken.None, TaskCreationOptions.DenyChildAttach,
TaskScheduler.Default);
Console.WriteLine(arg.Value); // Prints '13' in the console :-/
await task;
Это сработало, но мне не нравится использование Task.Factory.StartNew
. Это метод старой школы, с плохой репутацией , опасный . Поэтому мой вопрос: возможно ли решить эту проблему, не используя метод Task.Factory.StartNew
?