Le nouveau modèle d'annulation est intégré dans le .NET Framework en plusieurs types, dont System.Threading.Tasks, System .Threading.Tasks.Task, System.Threading.Tasks.Task et System.Linq.ParallelEnumerable
Voici un exemple de votre problème. Ce code sera toujours bloqué parce que le code appelant prend d'abord un verrou, puis la tâche bloquée tente d'acquérir le même verrou.
public void Example()
{
object sync = new Object();
lock (sync)
{
CancellationTokenSource canceller = new CancellationTokenSource();
ManualResetEvent started = new ManualResetEvent(false);
Task deadlocked = Task.Factory.StartNew(() =>
{
started.Set();
// EVIL CODE: This will ALWAYS deadlock
lock(sync) { };
},
canceller.Token);
// Make sure task has started.
started.WaitOne();
canceller.Cancel();
try
{
// Wait for task to cancel.
deadlocked.Wait();
}
catch (AggregateException ex)
{
// Ignore canceled exception. SIMPLIFIED!
if (!(ex.InnerException is TaskCanceledException))
throw;
}
}
}
L'annulation de tâche dans la TPL est coopérative. En d'autres termes, cela bloquera toujours le processus car rien ne gèrera le jeton d'annulation étant donné que le thread de la tâche est verrouillé.
Il y a un moyen de contourner cela, mais il repose toujours sur les auteurs du code non fiable à faire la bonne chose:
public static void Example2()
{
Mutex sync = new Mutex(true);
CancellationTokenSource canceller = new CancellationTokenSource();
bool started = false;
Task deadlocked = Task.Factory.StartNew(() =>
{
started = true;
// EVIL CODE: This will ALWAYS deadlock
WaitHandle.WaitAny(new WaitHandle[] { canceller.Token.WaitHandle, sync });
},
canceller.Token);
// Make sure task has started.
while (!started) { }
canceller.Cancel();
try
{
// Wait for task to cancel.
deadlocked.Wait();
}
catch (AggregateException ex)
{
// Ignore canceled exception. SIMPLIFIED!
if (!(ex.InnerException is TaskCanceledException))
throw;
}
}
Points à noter; l'annulation est coopérative. Vous pouvez utiliser Token.WaitHandle pour obtenir un handle et l'attendre avec le ou les handle (s) des autres primitives de synchronisation. Mutex est beaucoup plus lent que Monitor (ou lock). En réalité, si vous ne faites pas suffisamment confiance à l'auteur du code pour les implémenter, alors je remettrai en question la logique de les faire tourner dans votre AppDomain sur le même thread.
Pour plus de détails, voir:
http://msdn.microsoft.com/en-us/library/dd997364.aspx
http://msdn.microsoft.com/en-us/library/dd537607.aspx
http://msdn.microsoft.com/en-us/library/ee191552.aspx
Merci, ceci est une information utile. Cependant, j'étais sous l'impression que c'était à la tâche d'écouter la demande d'annulation et de lancer OperationCancelledException seule si elle ne pouvait pas terminer complètement. Je vais essayer votre code quand j'ai une chance cet après-midi. –
A partir du premier lien, "Les auditeurs peuvent être notifiés des demandes d'annulation par interrogation, enregistrement de rappel ou attente sur les poignées d'attente." Tellement efficacement Task.Wait provoque l'écoute se produire. –
Voir: http://stackoverflow.com/questions/2293976/how-and-if-to-write-a-single-consumer-queue-using-the-task-parallel-library/2779208#2779208 pour un exemple de utiliser IsCancellationRequested pour vérifier l'annulation et y répondre. –