Я учу своих друзей ключевому слову async-await и до сих пор приводил им несколько примеров.
static async Task Do(Func<Task> job)
{
// some algorithm depends on job parameter.
await job();
}
Я думаю, что этот пример практически полезен во многих сценариях.
Есть ли польза от методов void с аргументами типа Func<Task>
или Func<Task<T>>
? Насколько я думаю, существование такого метода имеет смысл, потому что блокировать асинхронные методы не рекомендуется. Всегда рады любым комментариям!
"Следующее не кажется полезным ИМХО." - это примерно так же полезно, как await job()
... Предполагается, что другая работа выполняется до/после вызова job()
, независимо от того, ожидается ли Task
, возвращаемое job()
, или нет.
Второй выполняет функцию job
и возвращает результат. Ничего больше/меньше
Я написал методы, которые принимают Func<Task> job
и запускают задание в другом потоке. Метод может возвращать или не возвращать собственный Task
, но не будет async
.
Ваш метод возврата void может быть не синхронным. Это может сделать что-то вроде отправки job
в другую ветку.
@ArtificialOdorlessArmpit Нет, я никогда не говорил, что ты будешь использовать job().Wait()
Task
может в конечном итоге использоваться с ContinueWith
, или он может ожидаться в методе async void
, или, возможно, он полностью игнорируется (как часто бывает Task
, возвращаемый из Task.Run
), или, может быть, он помещается в очередь, а поток-потребитель позже ожидает его. .
@canton7: Извините, я не могу понять, что вы только что объяснили, без кода. Не могли бы вы опубликовать простейший код в качестве ответа?
Да, использование неасинхронных методов, выполняющих делегаты, может быть полезным, например, запросы LINQ, предоставляемые .Net.
Иногда вы хотите, чтобы приложение блокировалось до тех пор, пока вызываемый объект не вернет определенный результат.
Возьмем, к примеру, метод Список.ForEach(Действие).
(ПРИМЕЧАНИЕ: это НЕТ, фактическая реализация будет в .Net!)
public void ForEach(Action<T> action) {
if (action is default(Action<T>))
throw new ArgumentNullException(nameof(action), "Action must not be null!");
foreach (var element in this) {
// Execute provided action (delegate)
action(T);
}
}
В этом случае вы хотите, чтобы цикл выполнялся синхронно, например, из-за безопасности потоков.
Другим примером может быть метод public static System.Collections.Generic.IEnumerable<TSource> Where<TSource> (this System.Collections.Generic.IEnumerable<TSource> source, Func<TSource,bool> predicate);
, который также будет работать синхронно из соображений безопасности потоков, поэтому ваши результаты возвращаются, как и ожидалось.
Вы также можете всегда вызывать методы из отдельного потока (или асинхронного метода), если это необходимо. Большинство консольных приложений работают синхронно, так как нет графического интерфейса, который должен реагировать.
РЕДАКТИРОВАТЬ
Другим примером синхронных методов с Func в качестве параметра может быть консольное приложение. Допустим, вы реализуете консольное приложение с зависимостью от библиотеки (которую вы не можете контролировать). Эта библиотека предоставляет только задачи. В консольном приложении вы обычно не используете/не нуждаетесь в асинхронных приложениях, поскольку нет графического интерфейса, который может блокироваться, и/или ваше приложение должно продолжать работу только после того, как пользователь введет ввод.
// External method returning a Task
public Task<int> DoFooAsync(object myParam);
public bool ParseMyFoo(Func<Task<int>> myTask) {
var result = myTask().Result;
if (result == 0xbadbeef || result == 0xf00dbabe) {
Console.WriteLine("Schrödingers takeaway");
}
}
public static void Main(string[] args) {
Console.WriteLine("Assesment: {0}", ParseMyFoo(DooFooAsync(Console.ReadLine()));
}
Почему вы взяли пример с Action
, а не Func<Task>
или Func<Task<T>>
?
Они по сути одинаковы, это был просто пример реализации. Я добавил второй пример, используя Func<T> внизу. Я добавлю еще код.
@canton7 Извините, вы правы. Забыл свойство Result.
Main
не сможет поймать какие-либо возможные исключения, выброшенные ParseMyFoo
, поэтому этот пример слишком плох для учебных целей.
Is there any usefulness of having void methods with arguments of type
Func<Task>
orFunc<Task<T>>
?
да. Это будет метод, который синхронно что-то делает с этим делегатом. Он не будет выполнять делегат напрямую.
Одним из примеров, который приходит на ум, является своего рода метод Add
для выполнения очереди асинхронной работы. Делегат будет выполняться в другом месте, в коде "обработчика очереди". Добавление делегата в очередь будет синхронной операцией, даже если сам делегат является асинхронным. Вот пример.
static Task Do(Func<Task> job) { return job(); }
?return DoSomethingElse(job)
?