Tasksheduling в С#

259
18 ноября 2017, 09:26

Заранее извиняюсь за столь длинный вопрос. Разбираюсь c Task и TaskScheduler. Я не уверен, что я правильно понимаю смысла существования TaskScheduler. Как я вижу ситуацию с ним, т.к TaskScheduler является абстрактным, мы не можем создавать его напрямую, поэтому у нас есть следующие два способа его создания:

  1. унаследовать TaskScheduler и переопределить метод QueueTask который должен запускать задачу отложено;
  2. получить экземпляр TaskScheduler с помощью TaskScheduler.Default.

Потом экземпляр TaskScheduler мы отправляем в конструктор TaskFactory и после этого мы на экземпляре TaskFactory вызывается метод StartNew, в который мы передаем нашу отложенную задачу. И тут StartNew вызывает QueueTask (метод который находится в TaskScheduler и который мы возможно переопределили) и вот QueueTask задает отложенный вызов нашей задачи которую мы передали в StartNew. Вот на основе этого возникли следующие ВОПРОСЫ:

  1. Исходя из выше сканого у меня сложилось впечатление, что TaskScheduler вовсе никакой не планировщик задач, а просто прослойка между запускам задачи и ее началом выполнения. Он просто делает так, что бы StartNew (метод TaskFactory) запускал не саму задачу, а метод QueueTask в котором я сам должен сделать всю логику планирования. Получается он не имеет какую-то внутреннюю логику по запуску задач отложено. Нет, я понимаю, он имеет потому, что я сам ее написал в методе QueueTask, но по факту TaskScheduler это лишь механизм внедрения МОЕГО собственного планировщика. Я правильно понимаю или же я чего-то не знаю?
  2. Если я создам TaskScheduler через TaskScheduler.Default какая реализация будет у QueueTask? Как я понял он просто сразу запускает задачу...
  3. В примерах с QueueTask кроме отложенного запуска задачи делают добавление этой задачи в очередь... Зачем? Мы к примеру задали отложенную задачу с помощью ThreadPool.RegisterWaitForSingleObject т.е все, мы отправили туда callback. Второй раз мы уже не можем запустить эту задачу, зачем нам ее хранить? Я лишь предполагаю, что мы ее храним лишь для возможного контроля ее, до запуска.
Answer 1
  1. Начнем с того, что Task -- это абстракция задачи, которая ничего не говорит о том, где и кем будет выполнена задача. Этим как раз управляет TaskScheduler. Как вы верно заметили, класс TaskScheduler является абстрактным, а значит, конкретная логика заключена в его наследниках. А вот TaskFactory (и метод Task.Run()) -- это действительно прослойка между задачей и планировщиком.
  2. Самый очевидный наследник -- TaskScheduler.Default. Это планировщик пула потоков. Он выполняет порученные задачи в потоках из пула потоков. Время старта задачи при этом никак не оговаривается. Второй пример -- это группа планировщиков, которые предоставляет текущий синхронизационный контекст -- TaskScheduler.FromCurrentSynchronizationContext (например, ASP.NET, WinForms или WPF). Соответственно задача, которая была передана, например, планировщику WinForms, будет выполнена в главном (UI) потоке.
  3. Покажите пример.

P.S. В 99% случаев достаточно вызывать Task.Run() и не связываться с TaskFactory.

Answer 2

Для начала, параллельно к TaskScheduler.Default есть ещё TaskScheduler.FromCurrentSynchronizationContext() (который в главном потоке WPF и WinForms создают scheduler, выполняющий задания в этом самом потоке). Кроме того, использовать TaskScheduler можно и в других местах, например, в Task.ContinueWith, ну и await тоже пользуется им под капотом.

Можно смотреть на TaskScheduler как на «прослойку» между отправкой таска на выполнение и самим выполнением. Но это собственно и есть смысл «планировки»: вы поставляете таск, а как и когда он будет запущен — вопрос, который решает планировщик (scheduler). То есть TaskScheduler — это не механизм внедрения вашего планировщика, это и есть планировщик!

Получается он не имеет какую-то внутреннюю логику по запуску задач отложено

Не получается. Ваш метод QueueTask — часть TaskScheduler'а, это и есть вызов внутренней логики по запуску задач!

Если я создам TaskScheduler через TaskScheduler.Default какая реализация будет у QueueTask

TaskScheduler.Default — планировщий на пуле потоков. Он просто выполнит ThreadPool.QueueUserWorkItem.

Из исходников .NET:

protected internal override void QueueTask(Task task)
{
    if ((task.Options & TaskCreationOptions.LongRunning) != 0)
    {
        // Run LongRunning tasks on their own dedicated thread.
        Thread thread = new Thread(s_longRunningThreadWork);
        thread.IsBackground = true; // Keep this thread from blocking process shutdown
        thread.Start(task);
    }
    else
    {
        // Normal handling for non-LongRunning tasks.
        bool forceToGlobalQueue = ((task.Options & TaskCreationOptions.PreferFairness) != 0);
        ThreadPool.UnsafeQueueCustomWorkItem(task, forceToGlobalQueue);
    }
}

В примерах с QueueTask кроме отложенного запуска задачи делают добавление этой задачи в очередь... Зачем?

Это нужно спрашивать у авторов примера — скорее всего, в коде примера видно, откуда ещё производится доступ к очереди. ThreadPoolTaskScheduler, например, в QueueTask ничего не добавляет.

READ ALSO
Заполнение шаблона сообщения

Заполнение шаблона сообщения

Пишу чатОтрисовал шаблон сообщения в usercontrol

270
Как перезапустить Awesomium?

Как перезапустить Awesomium?

Использую awesomium вместо стандартного компонента браузераНужно изменить user agent

257
Есть ли возможность извлечь данные из файла .dwg, используя C#(желательно в excel)?

Есть ли возможность извлечь данные из файла .dwg, используя C#(желательно в excel)?

Нужно достать параметры некоторого объекта изdwg файла

252