Tworzenie wątków - Task.Factory.StartNew vs New Thread ()

102

Właśnie dowiaduję się o nowych bibliotekach Threading i Parallel w .Net 4

W przeszłości utworzyłbym nowy wątek w ten sposób (jako przykład):

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

Teraz mogę:

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

Jaka jest różnica, jeśli w ogóle?

Dzięki

Jon
źródło
1
Musisz trochę się martwić, jak działa harmonogram puli wątków. Może to mieć duże znaczenie, ale wszystko zależy od tego, co faktycznie robisz w wątku.
Hans Passant

Odpowiedzi:

79

Jest duża różnica. Zadania są planowane w puli wątków i mogą być nawet wykonywane synchronicznie, jeśli są odpowiednie.

Jeśli pracujesz w tle przez długi czas, powinieneś to określić, używając odpowiedniej opcji zadania.

Bibliotekę równoległą zadań należy preferować zamiast jawnej obsługi wątków, ponieważ jest ona bardziej zoptymalizowana. Masz również więcej funkcji, takich jak Kontynuacja.

sanosdole
źródło
5
Nie, nie ma. Po prostu rozpoczyna zadania. Może to umieścić zadanie w kolejce w puli wątków lub wykonać je synchronicznie. W TPL chodzi o uwolnienie Cię od samodzielnego zarządzania wątkami / współbieżnością i korzystania z tego, co najlepsze dla Twojej platformy (np. Wykorzystanie rdzeni)
sanosdole
10
Istnieje opcja TaskCreationOptions.LongRunning, która zawsze tworzy kolejny wątek, ale chodzi o to, dlaczego potrzebujesz kolejnego wątku? Jeśli chcesz po prostu zrobić coś równolegle (Main robi coś podczas działania zadania), lepiej pozwolić zoptymalizowanej bibliotece na podjęcie decyzji, jak wykorzystać zasoby systemowe, takie jak wątki, aby zrobić to w najbardziej efektywny sposób.
sanosdole
3
W tym artykule msdn opisano sposób planowania zadań. Obejmuje longrunning i inlining (wykonanie synchroniczne). msdn.microsoft.com/en-us/library/dd997402.aspx
sanosdole
2
@sming Chodzi o to, że chcesz przetwarzać współbieżnie (nie blokując interfejsu użytkownika), a nie że chcesz nowy wątek. ThreadPool nie blokuje wątku interfejsu użytkownika, ale zarządza wątkami w tle znacznie wydajniej niż można to zrobić ręcznie, tworząc wątki. To jest zmiana w sposobie myślenia, którą wprowadza OC. Nie myśl o wątkach, myśl o równoległych zadaniach.
sanosdole
4
@sming Przepraszamy, to zdanie było trochę zbyt wulgarne. Synchroniczne wykonywanie zadań nazywa się inlining. Podczas planowania zadania w puli wątków (domyślny harmonogram) z poziomu wątku interfejsu użytkownika, nie nastąpi. Wystąpi tylko wtedy, gdy harmonogram otoczenia („TaskScheduler.Current”) jest taki sam jak harmonogram zadania, które wywołujesz „.Wait ()”. Ponieważ '.Wait ()' blokuje, i tak będzie blokować interfejs użytkownika. Krótko: nie wywołuj czekaj i nie zostanie wykonane synchronicznie.
sanosdole
74

Zadanie zapewnia wszystkie zalety interfejsu API zadań:

  • Dodawanie kontynuacji ( Task.ContinueWith)
  • Czekam na ukończenie wielu zadań (wszystkich lub dowolnych)
  • Wychwytywanie błędów w zadaniu i późniejsze ich przesłuchanie
  • Przechwytywanie anulowania (i umożliwianie określenia anulowania na początek)
  • Potencjalnie mający wartość zwracaną
  • Używanie await w C # 5
  • Lepsza kontrola nad planowaniem (jeśli ma to być długotrwałe, powiedz to podczas tworzenia zadania, aby harmonogram zadań mógł to uwzględnić)

Zwróć uwagę, że w obu przypadkach możesz nieco uprościć swój kod za pomocą konwersji grup metod:

DataInThread = new Thread(ThreadProcedure);
// Or...
Task t = Task.Factory.StartNew(ThreadProcedure);
Jon Skeet
źródło
8
+1. Chciałbym dodać, że Threadjest to bardzo niski poziom w porównaniu do Task(mam post na blogu, który zawiera szczegółowe informacje). W Grand Rapids DevDay wygłaszam wykład w rodzaju „używania Zadań w prawdziwym świecie” . Wykład nazywa się „Thread is Dead”, ponieważ nie ma już takiej potrzeby Thread(chyba że wdrażasz a TaskScheduler).
Stephen Cleary
@StephenCleary, zakładam, że masz na myśli Threadmartwy, jeśli chodzi o użycie go jako wątku w tle?
odpływ
1
@ebb: Nie, zajmuję silniejszą pozycję opisaną w moim pierwszym komentarzu. Nic nie Threadmożna zrobić (lub BackgroundWorker), czego nie można zrobić bardziej elegancko Taski stosownie TaskScheduler.
Stephen Cleary
1
@StephenCleary, Jak utworzyłbyś dedykowany wątek bez użycia Thread?
odpływ
4
@ebb: „Dedykowany wątek” nie jest dla mnie jasne. Jeśli chcesz, aby a Taskdziałał na konkretnym wątku, użyj odpowiedniego TaskScheduler- np AsyncContextThread. Jednak zwykle nie jest to konieczne; SynchronizationContext, ThreadPool, i ConcurrentExclusiveSchedulerPairplaniści są wystarczające dla większości programów.
Stephen Cleary,
12

W pierwszym przypadku po prostu rozpoczynasz nowy wątek, podczas gdy w drugim przypadku wchodzisz do puli wątków.

Zadaniem puli wątków jest udostępnianie i odtwarzanie wątków. Pozwala to uniknąć utraty kilku milisekund za każdym razem, gdy potrzebujemy stworzyć nowy wątek.

Dostęp do puli wątków można uzyskać na kilka sposobów:

  • z TPL (Task Parallel Library), tak jak to zrobiłeś
  • wywołując ThreadPool.QueueUserWorkItem
  • wywołując BeginInvoke na delegacie
  • podczas korzystania z BackgroundWorker
alexandrekow
źródło
1

Twój pierwszy blok kodu mówi CLR, aby utworzył dla Ciebie wątek (powiedzmy T), który można uruchomić jako tło (użyj wątków puli wątków podczas planowania T). Krótko mówiąc, jawnie prosisz środowisko CLR o utworzenie wątku, abyś mógł coś zrobić i wywołanie metody Start () w wątku, aby rozpocząć.

Twój drugi blok kodu robi to samo, ale deleguje (niejawnie przekazuje) odpowiedzialność za tworzenie wątku (w tle - który ponownie jest uruchamiany w puli wątków) i wątek początkowy za pomocą metody StartNew w implementacji fabryki zadań.

To jest szybka różnica między podanymi blokami kodu. Powiedziawszy to, istnieje kilka szczegółowych różnic, które możesz znaleźć w Google lub zobaczyć inne odpowiedzi od innych współtwórców.

s_nair
źródło