Nie możesz. Zadania używają wątków w tle z puli wątków. Nie zaleca się również anulowania wątków metodą Abort. Możesz rzucić okiem na następujący post na blogu, który wyjaśnia właściwy sposób anulowania zadań za pomocą tokenów anulowania. Oto przykład:
classProgram{staticvoidMain(){var ts =newCancellationTokenSource();CancellationToken ct = ts.Token;Task.Factory.StartNew(()=>{while(true){// do some heavy work hereThread.Sleep(100);if(ct.IsCancellationRequested){// another thread decided to cancelConsole.WriteLine("task canceled");break;}}}, ct);// Simulate waiting 3s for the task to completeThread.Sleep(3000);// Can't wait anymore => cancel this task
ts.Cancel();Console.ReadLine();}}
Niezłe wyjaśnienie. Mam pytanie, jak to działa, gdy nie mamy metody anonimowej w Task.Factory.StartNew? jak Task.Factory.StartNew (() => ProcessMyMethod (), cancellationToken)
Prerak K
61
co się stanie, jeśli pojawi się wywołanie blokujące, które nie powróci do wykonywanego zadania?
mehmet6parmak
3
@ mehmet6parmak Myślę, że jedyne, co możesz wtedy zrobić, to Task.Wait(TimeSpan / int)nadać mu (czasowy) termin z zewnątrz.
Mark
2
Co się stanie, jeśli mam własną klasę do zarządzania wykonywaniem metod wewnątrz nowej Task? Coś jak: public int StartNewTask(Action method). Wewnątrz StartNewTaskmetody utworzyć nowy Taskprzez: Task task = new Task(() => method()); task.Start();. Więc jak mogę zarządzać CancellationToken? Chciałbym również wiedzieć, czy Threadmuszę zaimplementować logikę, aby sprawdzić, czy są jakieś zadania, które nadal się zawieszają, i zabij je, kiedy Form.Closing. Z Threadsużywam Thread.Abort().
Cheshire Cat
O mój Boże, co za zły przykład! Jest to prosty warunek logiczny, oczywiście jest to pierwszy, jakiego można by spróbować! Ale tj. Mam funkcję w osobnym zadaniu, której zakończenie może zająć dużo czasu i najlepiej byłoby, gdyby nie wiedziała nic o wątkowaniu lub czymkolwiek. Jak więc anulować tę funkcję za twoją radą?
Hi-Angel
32
Przerwanie zadania jest łatwe, jeśli przechwycisz wątek, w którym zadanie jest uruchomione. Oto przykładowy kod, który to demonstruje:
voidMain(){Thread thread =null;Task t =Task.Run(()=>{//Capture the thread
thread =Thread.CurrentThread;//Simulate work (usually from 3rd party code)Thread.Sleep(1000);//If you comment out thread.Abort(), then this will be displayedConsole.WriteLine("Task finished!");});//This is needed in the example to avoid thread being still NULLThread.Sleep(10);//Cancel the task by aborting the thread
thread.Abort();}
Użyłem Task.Run (), aby pokazać najczęstszy przypadek użycia tego - korzystając z wygody Tasks ze starym kodem jednowątkowym, który nie używa klasy CancellationTokenSource do określenia, czy należy go anulować, czy nie.
Dzięki za ten pomysł. Zastosowano to podejście, aby zaimplementować limit czasu dla jakiegoś zewnętrznego kodu, który nie ma CancellationTokenwsparcia ...
Christoph Fink
7
AFAIK thread.abort pozostawi nieznany twój stos, może być nieprawidłowy. Nigdy tego nie próbowałem, ale myślę, że rozpoczynając wątek w oddzielnej domenie aplikacji, ten wątek.abort zostanie zapisany! Poza tym cały wątek jest marnowany w twoim rozwiązaniu tylko po to, aby przerwać jedno zadanie. Nie musiałbyś używać zadań, ale wątków na pierwszym miejscu. (głos przeciwny)
Martin Meeser
1
Jak pisałem - to rozwiązanie to ostateczność, którą można rozważyć w pewnych okolicznościach. Oczywiście CancellationTokennależy wziąć pod uwagę lub nawet prostsze rozwiązania, które są wolne od warunków wyścigowych. Powyższy kod ilustruje tylko metodę, a nie obszar użycia.
Florian Rappl
8
Myślę, że takie podejście może mieć nieznane konsekwencje i nie polecałbym go w kodzie produkcyjnym. Zadania nie zawsze są wykonywane w innym wątku, co oznacza, że można je uruchomić w tym samym wątku, co ten, który je utworzył, jeśli planista tak zdecyduje (co oznacza, że główny wątek zostanie przekazany do threadzmiennej lokalnej). W swoim kodzie możesz zakończyć przerwanie głównego wątku, co nie jest tym, czego naprawdę chcesz. Być może sprawdzenie, czy wątki są takie same przed przerwaniem, byłoby dobrym pomysłem, jeśli nalegasz na przerwanie
Ivaylo Slavov
10
@Martin - Po zbadaniu tego pytania na SO, widzę, że przegłosowałeś kilka odpowiedzi, które używają Thread.Abort do zabijania zadań, ale nie podałeś żadnych alternatywnych rozwiązań. Jak można zabić kod innej firmy, który nie obsługuje anulowania i działa w zadaniu ?
Gordon Bean,
32
Jak sugeruje ten post , można to zrobić w następujący sposób:
intFoo(CancellationToken token){Thread t =Thread.CurrentThread;
using (token.Register(t.Abort)){// compute-bound work here}}
Chociaż to działa, nie zaleca się stosowania takiego podejścia. Jeśli możesz kontrolować kod, który jest wykonywany w zadaniu, lepiej skorzystaj z odpowiedniej obsługi anulowania.
+1 za podanie innego podejścia i wskazanie jego wad. Nie wiedziałem, że można to zrobić :)
Joel
1
Dzięki za rozwiązanie! Możemy po prostu przekazać token do metody i anulować źródło tokenu, zamiast w jakiś sposób pobierać wystąpienie wątku z metody i bezpośrednio przerywać to wystąpienie.
Takie rzeczy są jednym z logistycznych powodów, dla których Abortsą przestarzałe. Przede wszystkim nie używaj Thread.Abort()do anulowania lub zatrzymywania wątku, jeśli jest to w ogóle możliwe. Abort()powinien być używany tylko do wymuszonego niszczenia wątku, który nie odpowiada na spokojniejsze prośby o zaprzestanie w odpowiednim czasie.
Biorąc to pod uwagę, musisz podać udostępniony wskaźnik anulowania, który jeden wątek ustawia i czeka, podczas gdy drugi wątek okresowo sprawdza i bezpiecznie kończy pracę. .NET 4 zawiera strukturę zaprojektowaną specjalnie do tego celu CancellationToken.
Nie powinieneś próbować tego robić bezpośrednio. Zaprojektuj swoje zadania do pracy z CancellationToken i anuluj je w ten sposób.
Ponadto zalecałbym zmianę głównego wątku, aby działał również za pośrednictwem CancellationToken. Telefonowanie Thread.Abort()to zły pomysł - może prowadzić do różnych problemów, które są bardzo trudne do zdiagnozowania. Zamiast tego ten wątek może używać tego samego anulowania, którego używają twoje zadania - i tego samego CancellationTokenSourcemożna użyć do wyzwolenia anulowania wszystkich zadań i głównego wątku.
Doprowadzi to do znacznie prostszego i bezpieczniejszego projektu.
Aby odpowiedzieć na pytanie Prerak K o to, jak używać CancellationTokens, gdy nie używasz metody anonimowej w Task.Factory.StartNew (), musisz przekazać CancellationToken jako parametr do metody, którą zaczynasz od StartNew (), jak pokazano w przykładzie MSDN tutaj .
na przykład
var tokenSource =newCancellationTokenSource();var token = tokenSource.Token;Task.Factory.StartNew(()=>DoSomeWork(1, token), token);staticvoidDoSomeWork(int taskNum,CancellationToken ct){// Do work here, checking and acting on ct.IsCancellationRequested where applicable, }
Po pierwsze, staram się uprzejmie anulować, korzystając z opcji Anulowanie .
Jeśli nadal działa (np. Z powodu błędu programisty), zachowaj się niewłaściwie i zabij go starą metodą Abort .
Sprawdź przykład poniżej:
privateCancellationTokenSource taskToken;privateAutoResetEvent awaitReplyOnRequestEvent =newAutoResetEvent(false);voidMain(){// Start a task which is doing nothing but sleeps 1sLaunchTaskAsync();Thread.Sleep(100);// Stop the taskStopTask();}/// <summary>/// Launch task in a new thread/// </summary>voidLaunchTaskAsync(){
taskToken =newCancellationTokenSource();Task.Factory.StartNew(()=>{try{//Capture the thread
runningTaskThread =Thread.CurrentThread;// Run the taskif(taskToken.IsCancellationRequested||!awaitReplyOnRequestEvent.WaitOne(10000))return;Console.WriteLine("Task finished!");}catch(Exception exc){// Handle exception}}, taskToken.Token);}/// <summary>/// Stop running task/// </summary>voidStopTask(){// Attempt to cancel the task politelyif(taskToken !=null){if(taskToken.IsCancellationRequested)return;else
taskToken.Cancel();}// Notify a waiting thread that an event has occurredif(awaitReplyOnRequestEvent !=null)
awaitReplyOnRequestEvent.Set();// If 1 sec later the task is still running, kill it cruellyif(runningTaskThread !=null){try{
runningTaskThread.Join(TimeSpan.FromSeconds(1));}catch(Exception ex){
runningTaskThread.Abort();}}}
Zadania mają pierwszą klasę obsługi anulowania za pomocą tokenów anulowania . Twórz zadania za pomocą tokenów anulowania i wyraźnie anuluj zadania za pomocą tych tokenów.
Możesz użyć, CancellationTokenaby kontrolować, czy zadanie zostanie anulowane. Czy mówisz o przerwaniu tego przed rozpoczęciem („nieważne, już to zrobiłem”), czy też o przerwaniu w środku? Jeśli to pierwsze, CancellationTokenmogą być pomocne; jeśli to drugie, prawdopodobnie będziesz musiał zaimplementować własny mechanizm "bail out" i sprawdzić w odpowiednich momentach wykonywania zadania, czy powinieneś szybko zawieść (nadal możesz użyć CancellationToken, aby ci pomóc, ale jest to trochę bardziej ręczne).
Zadania są wykonywane w puli wątków (przynajmniej jeśli używasz domyślnej fabryki), więc przerwanie wątku nie ma wpływu na zadania. Aby uzyskać informacje na temat przerywania zadań, zobacz Anulowanie zadań w witrynie msdn.
Próbowałem, CancellationTokenSourceale nie mogę tego zrobić. I zrobiłem to na swój własny sposób. I to działa.
namespace Blokick.Provider{publicclassSignalRConnectProvider{publicSignalRConnectProvider(){}publicboolIsStopRequested{get;set;}=false;//1-)This is important and default `false`.publicasyncTask<string>ConnectTab(){string messageText ="";for(int count =1; count <20; count++){if(count ==1){//Do stuff.}try{//Do stuff.}catch(Exception ex){//Do stuff.}if(IsStopRequested)//3-)This is important. The control of the task stopping request. Must be true and in inside.{return messageText ="Task stopped.";//4-) And so return and exit the code and task.}if(Connected){//Do stuff.}if(count ==19){//Do stuff.}}return messageText;}}}
I kolejna klasa wywołująca metodę:
namespace Blokick.Views{[XamlCompilation(XamlCompilationOptions.Compile)]publicpartialclassMessagePerson:ContentPage{SignalRConnectProvider signalR =newSignalRConnectProvider();publicMessagePerson(){InitializeComponent();
signalR.IsStopRequested=true;// 2-) And this. Make true if running the task and go inside if statement of the IsStopRequested property.if(signalR.ChatHubProxy!=null){
signalR.Disconnect();}LoadSignalRMessage();}}}
Możesz przerwać zadanie tak jak wątek, jeśli możesz spowodować utworzenie zadania we własnym wątku i wywołanie Abortjego Threadobiektu. Domyślnie zadanie jest uruchamiane w wątku puli wątków lub wątku wywołującym - z których zwykle nie chcesz przerwać.
Aby upewnić się, że zadanie otrzymuje własny wątek, utwórz niestandardowy harmonogram pochodzący z TaskScheduler. W swojej implementacji QueueTaskutwórz nowy wątek i użyj go do wykonania zadania. Później możesz przerwać wątek, co spowoduje zakończenie zadania w stanie błędu z rozszerzeniem ThreadAbortException.
Thread.AbortMetoda powinna być stosowana z ostrożnością. W szczególności, gdy wywołujesz go w celu przerwania wątku innego niż bieżący wątek, nie wiesz, jaki kod został wykonany lub którego wykonanie nie powiodło się, gdy zostanie zgłoszony wyjątek ThreadAbortException , ani nie możesz być pewien stanu aplikacji lub stanu aplikacji i użytkownika że jest odpowiedzialny za zachowanie. Na przykład wywołanie Thread.Abortmoże uniemożliwić wykonanie konstruktorów statycznych lub zwolnienie niezarządzanych zasobów.
Ten kod kończy się niepowodzeniem z wyjątkiem czasu wykonywania: System.InvalidOperationException: RunSynchronously nie może być wywoływana dla zadania, które zostało już uruchomione.
Theodor Zoulias
1
@TheodorZoulias Dobry chwyt. Dzięki. Poprawiłem kod i ogólnie poprawiłem odpowiedź.
Edward Brey
1
Tak, to naprawiło błąd. Innym zastrzeżeniem, o którym prawdopodobnie należy wspomnieć, jest to, że Thread.Abortnie jest obsługiwane na platformie .NET Core. Próba użycia go tam kończy się wyjątkiem: System.PlatformNotSupportedException: przerywanie wątku nie jest obsługiwane na tej platformie. Trzecim zastrzeżeniem jest to, że SingleThreadTaskSchedulernie można ich skutecznie używać w przypadku zadań typu obietnica, innymi słowy w przypadku zadań tworzonych z asyncdelegatami. Na przykład osadzony await Task.Delay(1000)działa w żadnym wątku, więc nie ma to wpływu na zdarzenia wątku.
Odpowiedzi:
Nie możesz. Zadania używają wątków w tle z puli wątków. Nie zaleca się również anulowania wątków metodą Abort. Możesz rzucić okiem na następujący post na blogu, który wyjaśnia właściwy sposób anulowania zadań za pomocą tokenów anulowania. Oto przykład:
źródło
Task.Wait(TimeSpan / int)
nadać mu (czasowy) termin z zewnątrz.Task
? Coś jak:public int StartNewTask(Action method)
. WewnątrzStartNewTask
metody utworzyć nowyTask
przez:Task task = new Task(() => method()); task.Start();
. Więc jak mogę zarządzaćCancellationToken
? Chciałbym również wiedzieć, czyThread
muszę zaimplementować logikę, aby sprawdzić, czy są jakieś zadania, które nadal się zawieszają, i zabij je, kiedyForm.Closing
. ZThreads
używamThread.Abort()
.Przerwanie zadania jest łatwe, jeśli przechwycisz wątek, w którym zadanie jest uruchomione. Oto przykładowy kod, który to demonstruje:
Użyłem Task.Run (), aby pokazać najczęstszy przypadek użycia tego - korzystając z wygody Tasks ze starym kodem jednowątkowym, który nie używa klasy CancellationTokenSource do określenia, czy należy go anulować, czy nie.
źródło
CancellationToken
wsparcia ...CancellationToken
należy wziąć pod uwagę lub nawet prostsze rozwiązania, które są wolne od warunków wyścigowych. Powyższy kod ilustruje tylko metodę, a nie obszar użycia.thread
zmiennej lokalnej). W swoim kodzie możesz zakończyć przerwanie głównego wątku, co nie jest tym, czego naprawdę chcesz. Być może sprawdzenie, czy wątki są takie same przed przerwaniem, byłoby dobrym pomysłem, jeśli nalegasz na przerwanieJak sugeruje ten post , można to zrobić w następujący sposób:
Chociaż to działa, nie zaleca się stosowania takiego podejścia. Jeśli możesz kontrolować kod, który jest wykonywany w zadaniu, lepiej skorzystaj z odpowiedniej obsługi anulowania.
źródło
Takie rzeczy są jednym z logistycznych powodów, dla których
Abort
są przestarzałe. Przede wszystkim nie używajThread.Abort()
do anulowania lub zatrzymywania wątku, jeśli jest to w ogóle możliwe.Abort()
powinien być używany tylko do wymuszonego niszczenia wątku, który nie odpowiada na spokojniejsze prośby o zaprzestanie w odpowiednim czasie.Biorąc to pod uwagę, musisz podać udostępniony wskaźnik anulowania, który jeden wątek ustawia i czeka, podczas gdy drugi wątek okresowo sprawdza i bezpiecznie kończy pracę. .NET 4 zawiera strukturę zaprojektowaną specjalnie do tego celu
CancellationToken
.źródło
Nie powinieneś próbować tego robić bezpośrednio. Zaprojektuj swoje zadania do pracy z CancellationToken i anuluj je w ten sposób.
Ponadto zalecałbym zmianę głównego wątku, aby działał również za pośrednictwem CancellationToken. Telefonowanie
Thread.Abort()
to zły pomysł - może prowadzić do różnych problemów, które są bardzo trudne do zdiagnozowania. Zamiast tego ten wątek może używać tego samego anulowania, którego używają twoje zadania - i tego samegoCancellationTokenSource
można użyć do wyzwolenia anulowania wszystkich zadań i głównego wątku.Doprowadzi to do znacznie prostszego i bezpieczniejszego projektu.
źródło
Aby odpowiedzieć na pytanie Prerak K o to, jak używać CancellationTokens, gdy nie używasz metody anonimowej w Task.Factory.StartNew (), musisz przekazać CancellationToken jako parametr do metody, którą zaczynasz od StartNew (), jak pokazano w przykładzie MSDN tutaj .
na przykład
źródło
Stosuję mieszane podejście do anulowania zadania.
Sprawdź przykład poniżej:
źródło
Zadania mają pierwszą klasę obsługi anulowania za pomocą tokenów anulowania . Twórz zadania za pomocą tokenów anulowania i wyraźnie anuluj zadania za pomocą tych tokenów.
źródło
Możesz użyć,
CancellationToken
aby kontrolować, czy zadanie zostanie anulowane. Czy mówisz o przerwaniu tego przed rozpoczęciem („nieważne, już to zrobiłem”), czy też o przerwaniu w środku? Jeśli to pierwsze,CancellationToken
mogą być pomocne; jeśli to drugie, prawdopodobnie będziesz musiał zaimplementować własny mechanizm "bail out" i sprawdzić w odpowiednich momentach wykonywania zadania, czy powinieneś szybko zawieść (nadal możesz użyć CancellationToken, aby ci pomóc, ale jest to trochę bardziej ręczne).W witrynie MSDN jest artykuł dotyczący anulowania zadań: http://msdn.microsoft.com/en-us/library/dd997396.aspx
źródło
Zadania są wykonywane w puli wątków (przynajmniej jeśli używasz domyślnej fabryki), więc przerwanie wątku nie ma wpływu na zadania. Aby uzyskać informacje na temat przerywania zadań, zobacz Anulowanie zadań w witrynie msdn.
źródło
Próbowałem,
CancellationTokenSource
ale nie mogę tego zrobić. I zrobiłem to na swój własny sposób. I to działa.I kolejna klasa wywołująca metodę:
źródło
Możesz przerwać zadanie tak jak wątek, jeśli możesz spowodować utworzenie zadania we własnym wątku i wywołanie
Abort
jegoThread
obiektu. Domyślnie zadanie jest uruchamiane w wątku puli wątków lub wątku wywołującym - z których zwykle nie chcesz przerwać.Aby upewnić się, że zadanie otrzymuje własny wątek, utwórz niestandardowy harmonogram pochodzący z
TaskScheduler
. W swojej implementacjiQueueTask
utwórz nowy wątek i użyj go do wykonania zadania. Później możesz przerwać wątek, co spowoduje zakończenie zadania w stanie błędu z rozszerzeniemThreadAbortException
.Użyj tego harmonogramu zadań:
Rozpocznij swoje zadanie w ten sposób:
Później możesz przerwać za pomocą:
Pamiętaj, że nadal obowiązuje zastrzeżenie dotyczące przerywania wątku :
źródło
Thread.Abort
nie jest obsługiwane na platformie .NET Core. Próba użycia go tam kończy się wyjątkiem: System.PlatformNotSupportedException: przerywanie wątku nie jest obsługiwane na tej platformie. Trzecim zastrzeżeniem jest to, żeSingleThreadTaskScheduler
nie można ich skutecznie używać w przypadku zadań typu obietnica, innymi słowy w przypadku zadań tworzonych zasync
delegatami. Na przykład osadzonyawait Task.Delay(1000)
działa w żadnym wątku, więc nie ma to wpływu na zdarzenia wątku.