Как запланировать задачу для будущего выполнения в библиотеке параллельных задач

Есть ли способ запланировать задачу для выполнения в будущем с помощью библиотеки параллельных задач?

Я понимаю, что мог бы сделать это с помощью методов pre-.NET4, таких как System.Threading.Timer... однако, если есть способ TPL сделать это, я бы предпочел остаться в рамках дизайна фреймворка. Однако я не могу найти его.

Спасибо.


person Slaggg    schedule 19.11.2010    source источник


Ответы (3)


Эта функция была представлена ​​в Async CTP, которая теперь включена в .NET 4.5. Выполнение этого следующим образом не блокирует поток, а возвращает задачу, которая будет выполняться в будущем.

Task<MyType> new_task = Task.Delay(TimeSpan.FromMinutes(5))
                            .ContinueWith<MyType>( /*...*/ );

(При использовании старых выпусков Async используйте статический класс TaskEx вместо Task)

person Glenn Slayden    schedule 21.11.2010
comment
Или просто ожидайте Task.Delay(TimeSpan.FromMinutes(5)); msdn.microsoft.com/en-us/library /hh194873(v=vs.110).aspx - person Chris Moschini; 29.11.2013
comment
+1, но есть ли альтернатива без сна для .NET 4? (Я использую немного переработанный подход, используя один поток с очередью приоритетов на основе кучи, которая просто запускает новые задачи, когда приходит их время, перепроектирован, поскольку управление очередью приоритетов не бесплатно) - person Milosz Krajewski; 02.03.2014
comment
@MiloszKrajewski Task.Delay не спит! Как правило, засыпание Task (потока) полностью противоречит цели дизайна и философии System.Threading.Tasks.Task и не рекомендуется. Сон никогда не станет основой встроенного .NET API, такого как Task.Delay; скорее он полагается на планировщик в ядре ОС для создания задачи, которая не выполняется, пока не истечет задержка. Кроме того, Крис предложил await, и в этом случае Delay может вообще не использовать дополнительный поток, так как async/await позволяют отдельным потокам вносить вклад в продвижение нескольких заблокированных операций. - person Glenn Slayden; 29.11.2017
comment
@GlennSlayden Я знаю, что теперь это древняя история, но вы пропустили один бит информации, я сказал: .NET 4. Task.Delay - это .NET 4.5. Или, говоря иначе, альтернатива в .NET 4, не использующая Sleep. Хотя уже не имеет значения. Уже перешел на .NET 4.6. - person Milosz Krajewski; 30.11.2017
comment
@MiloszKrajewski Действительно «древняя история» - и для этого потребовалось бы, чтобы ваш проект разрешал использование кода предварительного просмотра, но было бы невозможно запустить асинхронную CTP (о которой я упоминал в первоначальном ответе) поверх .NET 4? - person Glenn Slayden; 10.06.2018

Вы можете написать свою собственную функцию RunDelayed. Это требует задержки и функции для запуска после завершения задержки.

    public static Task<T> RunDelayed<T>(int millisecondsDelay, Func<T> func)
    {
        if(func == null)
        {
            throw new ArgumentNullException("func");
        }
        if (millisecondsDelay < 0)
        {
            throw new ArgumentOutOfRangeException("millisecondsDelay");
        }

        var taskCompletionSource = new TaskCompletionSource<T>();

        var timer = new Timer(self =>
        {
            ((Timer) self).Dispose();
            try
            {
                var result = func();
                taskCompletionSource.SetResult(result);
            }
            catch (Exception exception)
            {
                taskCompletionSource.SetException(exception);
            }
        });
        timer.Change(millisecondsDelay, millisecondsDelay);

        return taskCompletionSource.Task;
    }

Используйте это так:

    public void UseRunDelayed()
    {
        var task = RunDelayed(500, () => "Hello");
        task.ContinueWith(t => Console.WriteLine(t.Result));
    }
person Mike Hadlow    schedule 05.07.2011
comment
Я считаю, что ваш таймер может быть собран мусором до выполнения, поскольку у вас нет внешней ссылки на него. - person newdayrising; 12.06.2012

Установите одноразовый таймер, который при срабатывании запускает задачу. Например, приведенный ниже код будет ждать пять минут перед запуском задачи.

TimeSpan TimeToWait = TimeSpan.FromMinutes(5);
Timer t = new Timer((s) =>
    {
        // start the task here
    }, null, TimeToWait, TimeSpan.FromMilliseconds(-1));

TimeSpan.FromMilliseconds(-1) делает таймер одноразовым, а не периодическим.

person Jim Mischel    schedule 19.11.2010
comment
Это работает? Похоже, что таймер мог получить GCd после того, как он вышел за рамки, и до того, как мое событие сработало... В любом случае, я надеялся на что-то родное для TPL (например, принятие задачи в качестве аргумента и разрешение управления задачами), которого, кажется, не существует. - person Slaggg; 20.11.2010
comment
Да, в этом примере таймер может быть собран. Объявите ссылку Timer в области, которая предотвратит преждевременный сбор. - person Jim Mischel; 20.11.2010
comment
ОК - спасибо за ваш ответ. Я упомянул в вопросе, что таймер будет работать; я надеялся на что-то, что изначально имело дело с объектами Task. Однако, спасибо! - person Slaggg; 20.11.2010