Создать завершенную задачу

197

Я хочу создать законченный Task(не Task<T>). Есть ли что-то встроенное в .NET для этого?

Связанный вопрос: Создать завершенную задачу <T>

Тимоти Шилдс
источник
2
It seems like the answer I'm getting from everyone is that using a garbage value like this is the correct way. That there isn't a way to do this without the garbage value is disappointing -- oh well.Как вы думаете, какие проблемы это имеет? Если вы кешируете один Taskфайл, тогда вся ваша программа занимает один дополнительный бит памяти. Это ничего . Кроме того, можно создать завершенную задачу, не делая этого, просто не будет ничего лучше.
Serv
10
О, мое разочарование не имеет ничего общего с необходимостью использования дополнительной памяти. Просто значения мусора в любом месте кода не элегантны.
Тимоти Шилдс
1
Обратите внимание, что сегодня есть ValueTaskдля завершенных задач (то есть для значений, которые у вас уже есть, так что код является по существу синхронным), что сэкономит вам распределение.
Nawfal

Ответы:

247

В новейшей версии .Net (v4.6) добавлена ​​только встроенная задача Task.CompletedTask :

Task completedTask = Task.CompletedTask;

Это свойство реализовано как синглтон без блокировки, поэтому вы почти всегда будете использовать одну и ту же завершенную задачу.

i3arnon
источник
1
Только что проверил последнюю версию VS 14 CTP и создал проект 4.5.3, Task.CompletedTaskвсе еще внутренний.
Питер Ричи
1
2. Либо вы не загрузили последнюю версию CTP (которая 4 и связана с этого сайта), либо не указали версию 4.5.3. Вот что у меня на машине . @PeterRitchie
i3arnon
Я создал виртуальную машину из образа Visual Studio 14 в Azure.
Питер Ричи
1
@PeterRitchie Из шаблона Azure VS14CTP4
i3arnon
Я работаю на Mac OS X с моно 5.4.1.7, и я получаю эту же ошибку. Как я могу это исправить?
Халед Аннажар
153

Task<T>неявно конвертируется в Task, так что просто получите завершенное Task<T>(с любым Tи любым значением) и используйте его. Вы можете использовать что-то вроде этого, чтобы скрыть факт, что где-то есть фактический результат.

private static Task completedTask = Task.FromResult(false);
public static Task CompletedTask()
{
    return completedTask;
}

Обратите внимание, что, поскольку мы не показываем результат, и задача всегда выполняется, мы можем кэшировать одну задачу и использовать ее повторно.

Если вы используете .NET 4.0, но у FromResultвас его нет, вы можете создать свой собственный, используя TaskCompletionSource:

public static Task<T> FromResult<T>(T value)
{
    var tcs = new TaskCompletionSource<T>();
    tcs.SetResult(value);
    return tcs.Task;
}
Servy
источник
8
Если вы используете 4.0, вы можете добавить библиотеку Microsoft.Bcl.Async, чтобы получить TaskEx.FromResult, а также другие полезные вещи, такие как WhenAll
Carl
@Servy Что это меняет от FromResult (false) и FromResult (true)?
Франческо Боницци
3
@FrancescoB. Если вы когда-либо смотрите на результат, то это изменит логическое значение результата. Если вы игнорируете результат, то что это за результат, не имеет значения.
Serv
66

Мой предпочтительный метод для этого - вызывать Task.WhenAll()без аргументов. В документации MSDN указано, что «если предоставленный массив / перечислимый не содержит задач, возвращаемая задача немедленно перейдет в состояние RanToCompletion, прежде чем она будет возвращена вызывающей стороне». Это звучит как то, что вы хотите.

Обновление: я нашел источник в Справочном источнике Microsoft ; там вы можете увидеть, что Task.WhenAll содержит следующее:

return (tasks.Length == 0) ? // take shortcut if there are no tasks upon which to wait
            Task.CompletedTask :
            new WhenAllPromise(tasks);

Таким образом, Task.CompletedTask действительно является внутренним, но он открывается, вызывая WhenAll () без аргументов.

Richiban
источник
9
в то время как он чувствует себя немного хакером, его поддерживают документы, так что я не знаю, мне это нравится!
Сара
2
Для меня и моего ограниченного кода .NET 4.5.2 это достаточно элегантно. Спасибо!
Стас Иванов
36

Я бы использовал Task.Delay(0). Внутренне он возвращает кэшированный экземпляр завершенного Task<T>. Это именно то, что текущий ответ предлагает делать в любом случае, только теперь вам не нужно кэшировать экземпляр самостоятельно, и при этом в вашем коде нет никаких ненужных значений мусора.

Вы можете подумать , вы можете использовать Task.Yield()вместо этого, но оказывается , что результат в Task.Yield()это не подтип Task, а результат Task.Delay(0)есть. Это одно из тонких различий между ними.

Гзак
источник
30

Вы можете использовать Task.FromResult (в .NET 4.5) для возврата завершенного Task<T>.

Если вам нужен неуниверсальный Task, вы всегда можете использовать Task.FromResult(0)или подобное, так Task<T>как это подкласс Task.

Рид Копси
источник
11

Для .Net 4.6 и выше используйте

return Task.CompletedTask;

Для более низкой версии вы можете использовать

return new Task(() => { });
Icen
источник
3
Для подхода до 4.6, осторожно! Вам нужно запустить задание, иначе оно никогда не будет выполнено! Как насчет использования return Task.Delay(0);вместо?
Микель
0

Как насчет:

#pragma warning disable 1998
    public async Task emptyTask() {
    }
#pragma warning restore 1998

Вы можете не включать подавление предупреждений, если вы не против.

Dorus
источник
Я полагаю, что маркировка метода по- asyncпрежнему создает весь аппарат конечного автомата, даже если вы его не используете, поэтому возврат пустого задания более эффективен для сценариев с ограниченными ресурсами.
Кэлвин Фишер