W wątku tworzę kilka System.Threading.Task
i rozpoczynam każde zadanie.
Kiedy robię a, .Abort()
aby zabić wątek, zadania nie są przerywane.
Jak mogę przesłać plik .Abort()
do moich zadań?
W wątku tworzę kilka System.Threading.Task
i rozpoczynam każde zadanie.
Kiedy robię a, .Abort()
aby zabić wątek, zadania nie są przerywane.
Jak mogę przesłać plik .Abort()
do moich zadań?
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:
class Program
{
static void Main()
{
var ts = new CancellationTokenSource();
CancellationToken ct = ts.Token;
Task.Factory.StartNew(() =>
{
while (true)
{
// do some heavy work here
Thread.Sleep(100);
if (ct.IsCancellationRequested)
{
// another thread decided to cancel
Console.WriteLine("task canceled");
break;
}
}
}, ct);
// Simulate waiting 3s for the task to complete
Thread.Sleep(3000);
// Can't wait anymore => cancel this task
ts.Cancel();
Console.ReadLine();
}
}
Task.Wait(TimeSpan / int)
nadać mu (czasowy) termin z zewnątrz.
Task
? Coś jak: public int StartNewTask(Action method)
. Wewnątrz StartNewTask
metody utworzyć nowy Task
przez: Task task = new Task(() => method()); task.Start();
. Więc jak mogę zarządzać CancellationToken
? Chciałbym również wiedzieć, czy Thread
muszę zaimplementować logikę, aby sprawdzić, czy są jakieś zadania, które nadal się zawieszają, i zabij je, kiedy Form.Closing
. Z Threads
używam Thread.Abort()
.
Przerwanie zadania jest łatwe, jeśli przechwycisz wątek, w którym zadanie jest uruchomione. Oto przykładowy kod, który to demonstruje:
void Main()
{
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 displayed
Console.WriteLine("Task finished!");
});
//This is needed in the example to avoid thread being still NULL
Thread.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.
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 przerwanie
Jak sugeruje ten post , można to zrobić w następujący sposób:
int Foo(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.
Takie rzeczy są jednym z logistycznych powodów, dla których Abort
są 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 CancellationTokenSource
moż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 = new CancellationTokenSource();
var token = tokenSource.Token;
Task.Factory.StartNew( () => DoSomeWork(1, token), token);
static void DoSomeWork(int taskNum, CancellationToken ct)
{
// Do work here, checking and acting on ct.IsCancellationRequested where applicable,
}
Stosuję mieszane podejście do anulowania zadania.
Sprawdź przykład poniżej:
private CancellationTokenSource taskToken;
private AutoResetEvent awaitReplyOnRequestEvent = new AutoResetEvent(false);
void Main()
{
// Start a task which is doing nothing but sleeps 1s
LaunchTaskAsync();
Thread.Sleep(100);
// Stop the task
StopTask();
}
/// <summary>
/// Launch task in a new thread
/// </summary>
void LaunchTaskAsync()
{
taskToken = new CancellationTokenSource();
Task.Factory.StartNew(() =>
{
try
{ //Capture the thread
runningTaskThread = Thread.CurrentThread;
// Run the task
if (taskToken.IsCancellationRequested || !awaitReplyOnRequestEvent.WaitOne(10000))
return;
Console.WriteLine("Task finished!");
}
catch (Exception exc)
{
// Handle exception
}
}, taskToken.Token);
}
/// <summary>
/// Stop running task
/// </summary>
void StopTask()
{
// Attempt to cancel the task politely
if (taskToken != null)
{
if (taskToken.IsCancellationRequested)
return;
else
taskToken.Cancel();
}
// Notify a waiting thread that an event has occurred
if (awaitReplyOnRequestEvent != null)
awaitReplyOnRequestEvent.Set();
// If 1 sec later the task is still running, kill it cruelly
if (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ć, 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
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, CancellationTokenSource
ale nie mogę tego zrobić. I zrobiłem to na swój własny sposób. I to działa.
namespace Blokick.Provider
{
public class SignalRConnectProvider
{
public SignalRConnectProvider()
{
}
public bool IsStopRequested { get; set; } = false; //1-)This is important and default `false`.
public async Task<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)]
public partial class MessagePerson : ContentPage
{
SignalRConnectProvider signalR = new SignalRConnectProvider();
public MessagePerson()
{
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 Abort
jego Thread
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 implementacji QueueTask
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 rozszerzeniem ThreadAbortException
.
Użyj tego harmonogramu zadań:
class SingleThreadTaskScheduler : TaskScheduler
{
public Thread TaskThread { get; private set; }
protected override void QueueTask(Task task)
{
TaskThread = new Thread(() => TryExecuteTask(task));
TaskThread.Start();
}
protected override IEnumerable<Task> GetScheduledTasks() => throw new NotSupportedException(); // Unused
protected override bool NotSupportedException(Task task, bool taskWasPreviouslyQueued) => throw new NotSupportedException(); // Unused
}
Rozpocznij swoje zadanie w ten sposób:
var scheduler = new SingleThreadTaskScheduler();
var task = Task.Factory.StartNew(action, cancellationToken, TaskCreationOptions.LongRunning, scheduler);
Później możesz przerwać za pomocą:
scheduler.TaskThread.Abort();
Pamiętaj, że nadal obowiązuje zastrzeżenie dotyczące przerywania wątku :
Thread.Abort
Metoda 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łanieThread.Abort
może uniemożliwić wykonanie konstruktorów statycznych lub zwolnienie niezarządzanych zasobów.
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, że SingleThreadTaskScheduler
nie można ich skutecznie używać w przypadku zadań typu obietnica, innymi słowy w przypadku zadań tworzonych z async
delegatami. 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.