Створення ниток - Task.Factory.StartNew vs нова тема ()


102

Я тільки дізнаюся про нові бібліотеки Threading і Paralellel у .Net 4

Раніше я створив би нову Тему (як приклад):

DataInThread = new Thread(new ThreadStart(ThreadProcedure));
DataInThread.IsBackground = true;
DataInThread.Start();

Тепер я можу:

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

Яка різниця, якщо така є?

Дякую


1
Вам потрібно буде трохи занепокоїтись про те, як працює планувальник пулу потоків. Це може дуже змінитись, але все залежить від того, що ви насправді робите всередині нитки.
Ганс Пасант

Відповіді:


79

Є велика різниця. Завдання заплановані на ThreadPool і навіть можуть бути виконані синхронно, якщо це доречно.

Якщо у вас є тривала фонова робота, вам слід вказати це, використовуючи правильний варіант завдання.

Вам слід віддати перевагу Паралельній бібліотеці завдань над явною обробкою потоків, оскільки вона оптимізована. Також у вас є інші функції, такі як Продовження.


5
Ні, це не так. Це тільки починає завдання. Це може спричинити завдання в пулі потоків або виконати його синхронно. TPL має на меті звільнити вас від керування потоками / одночасністю використання найкращого для вашої платформи (наприклад, використання ядер)
sanosdole

10
Є варіант TaskCreationOptions.LongRunning, який завжди створить інший потік, але вся справа в тому, навіщо вам потрібна інша нитка? Якщо ви просто хочете зробити щось паралельно (Main робить що-небудь, поки виконується завдання), то краще дозволити оптимізованій бібліотеці вирішити, як використовувати системні ресурси, як потоки, щоб це зробити найбільш ефективним способом.
sanosdole

3
У цій статті MSdn описано, як плануються завдання. Він охоплює довготривалість і вкладення (синхронне виконання). msdn.microsoft.com/en-us/library/dd997402.aspx
sanosdole

2
@sming Справа в тому, що ви хочете одночасно обробляти (не блокуючи інтерфейс користувача) не те, що вам потрібно новий потік. ThreadPool не блокує потік інтерфейсу, але керувати потоками фону набагато ефективніше, ніж ви могли зробити це вручну, створивши нитки. Це зміна розумового процесу, яке вводить TPL. Не думайте нитки, не думайте одночасно.
sanosdole

4
@sming Вибачте, це речення було занадто грубим. Синхронне виконання завдань називається вбудовою. При плануванні завдання в пулі потоків (планувальник за замовчуванням) з потоку інтерфейсу користувача воно не відбудеться. Це відбудеться лише в тому випадку, якщо планувальник оточуючого середовища ("TaskScheduler.Current") такий же, як планувальник завдання, яке ви викликаєте ".Wait ()" on. Оскільки ".Wait ()" блокується, він все одно блокує інтерфейс користувача. Коротко: Не чекайте дзвінка, і він не буде виконуватися синхронно.
sanosdole

74

Завдання дає вам усю користь API завдання:

  • Додавання продовження ( Task.ContinueWith)
  • Очікування завершення декількох завдань (усіх або будь-яких)
  • Захоплення помилок у завданні та допит їх пізніше
  • Захоплення скасування (і дозволяє вказати скасування для початку)
  • Потенційно має зворотне значення
  • Використовуючи функцію wait в C # 5
  • Кращий контроль за плануванням (якщо це буде тривалим, скажіть це, коли ви створюєте завдання, щоб планувальник завдань міг це врахувати)

Зауважте, що в обох випадках ви можете зробити свій код трохи простішим за допомогою групових перетворень:

DataInThread = new Thread(ThreadProcedure);
// Or...
Task t = Task.Factory.StartNew(ThreadProcedure);

8
+1. Я хотів би додати, що Threadце дуже низький рівень порівняно з Task(у мене є допис у блозі, який детально описується). Я веду розмову "Використання завдань у реальному світі" на Grand Rapids DevDay . Розмова називається "Нитка мертва", тому що в цьому більше немає потреби Thread(якщо ви не реалізуєте TaskScheduler).
Стівен Клірі

@StephenCleary, я припускаю, що ви маєте на увазі Threadмертву, якщо мова йде про використання в якості фонової нитки?
відплив

1
@ebb: Ні, я займаю сильнішу позицію, описану в моєму першому коментарі. Нічого не Threadможна зробити (або BackgroundWorker), що не можна зробити більш елегантно Taskі відповідно TaskScheduler.
Стівен Клірі

1
@StephenCleary, як би ви створили виділений потік, не використовуючи його Thread?
відплив

4
@ebb: "Виділена нитка" мені незрозуміла. Якщо ви хочете Taskпрацювати на певну тему, а потім використовувати відповідний TaskScheduler- наприклад, AsyncContextThread. Однак зазвичай це не потрібно; SynchronizationContext, ThreadPoolІ ConcurrentExclusiveSchedulerPairпланувальники досить для більшості програм.
Стівен Клірі

12

У першому випадку ви просто починаєте новий потік, а в другому випадку ви входите в пул потоків.

Завдання пулу потоків - це спільне використання та переробка потоків. Це дозволяє уникнути втрати кількох мілісекунд щоразу, коли нам потрібно створити нову нитку.

Існує кілька способів ввести пул потоків:

  • з TPL (паралельною бібліотекою завдань), як ви
  • зателефонувавши ThreadPool.QueueUserWorkItem
  • зателефонувавши на BeginInvoke на делегата
  • коли ви використовуєте BackgroundWorker

1

Ваш перший блок коду повідомляє CLR створити для вас Thread (скажімо, T), який можна запустити як фон (використовуйте нитки пулу потоків при плануванні T). Коротко кажучи, ви явно просите CLR створити потік для того, щоб зробити щось, і викликаєте метод Start () для потоку для запуску.

Ваш другий блок коду виконує те саме, але делегує (неявна передача) відповідальність за створення потоку (фонового зображення, який знову запускається в пулі потоків) та стартового потоку методом StartNew у реалізації заводу "Завдання".

Це швидка різниця між даними кодовими блоками. Сказавши це, є кілька детальних розбіжностей, на які можна переглядати Google або бачити інші відповіді від моїх колег.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.