Поиск на сайте
Главная Журнал Форум Wiki DRKB Страны мира

"Task.Factory.StartNew" против "new Task(...).Start"

В TPL(Task Parallel Library) есть несколько способов создания и запуска новой задачи. Один из них заключается в использовании конструктора для задачи а затем в вызове метода Start, например,

new Task(...).Start();

а другой в использовании StartNew в TaskFactory, например,

Task.Factory.StartNew(...);

Естевственно, возникает вопрос - чем отличаются эти два способа и какой из них лучше использовать в каких ситуациях ? В общем и целом, я всегда рекомендую использовать Task.Factory.StartNew, если конкретная ситуация не содержит веских причин для использования конструктора с последующим вызовом Start. Есть несколько причин, по которым я рекомендую это. С одной стороны, это в целом более эффективно. Например, используя метод Start нам необходимо делать синхронизацию, чтобы защитить задачу от множественного запуска и не дать другому процессу одновременно вызвать метод Start. В случае использования Task.Factory.StartNew мы уверены, что задача уже запланирована со ссылкой на наш код, что исключает последующие вызовы Start (все попытки вызова Start будут возвращать ошибку). Таким образом, для StartNew мы сможем избежать дополнительных затрат, связанных с синхронизацией.

Однако, в некоторых случаях создание экземпляра новой задачи, а затем её запуск является полезным и даже необходимым условием. Одним из таких примеров является наследование Task. Это непростой случай и потребность в наследовании от Task возникает крайне редко, но тем не менее, если единственный способ - это запуск наследованной задачи, то делать это прийдётся через Start, так как вызовы Task.Factory.StartNew в таком случае будут всегда возвращать конкретную Task или типы Task. Другой, еще более сложный пример заключается в необходимости для тела задачи иметь доступ к ссылке на саму себя, например, если задаче необходимо зашедулить саму себя. Решить такую задачу можно попытаться следующим способом:

    Task t = null;
    t = Task.Factory.StartNew(() =>
    {
        ...
        t.ContinueWith(...);
    });

Этот код, ошибочен, так как существует вероятность того, что ThreadPool подхватит запланированное задание и выполнит его до того как ссылка на Задачу (сохранённая в t) будет возвращена из StartNew. Если это произойдет, то тело задачи увидит значение t равное null. Один из способов исправить, это создание и запуск отдельной задачи, например:

    Task t = null;
    t = new Task(() =>
    {
        ...
        t.ContinueWith(...);
    });
    t.Start();

Теперь мы видим, что t правильно инициализирована во время начала работы тела задачи, так как сперва был создан экземпляр задачи. Короче говоря, есть конечно случаи, когда использование подхода "new Task(...).Start()" оправдано. Однако если Вы не знаете в данный момент какой из способов лучше использовать, то советую отдать предпочтение TaskFactory.StartNew.




Основные разделы сайта


 

Реклама