6

Je voudrais commencer à utiliser le Task Parallel Library, car il s'agit du cadre recommandé pour l'exécution d'opérations asynchrones. Une chose que je n'ai pas été capable de trouver est n'importe quel moyen d'Abort forcé, tel que Thread.Abort fournit.Tuer une tâche en interblocage dans .NET 4 TPL

Ma préoccupation particulière est que je planifie des tâches exécutant du code que je ne souhaite pas totalement approuver. En particulier, je ne peux pas être sûr que ce code non fiable ne bloquera pas et je ne peux donc pas être certain si un programme Tâche I utilisant ce code sera complet. Je veux rester à l'écart de la véritable isolation de AppDomain (en raison de la surcharge et de la complexité du marshaling), mais je ne veux pas non plus laisser un thread de tâche traîner, impasse. Y a-t-il un moyen de faire cela en TPL?

Répondre

9

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

+0

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. –

+0

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. –

+0

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. –

-4

Vous appelez simplement Task.Wait(timespanToWait).

Si la tâche n'est pas terminée après le délai spécifié, elle est annulée.

+0

Merci. Ce n'était pas du tout évident à partir de la documentation, mais il est logique quand vous pensez que les Tâches vous isolent des détails sur la manière dont les Tâches sont planifiées et gérées. Savez-vous si je dois faire quelque chose de spécial après que l'attente soit revenue avant que je puisse en toute sécurité Disposer de la tâche? –

+11

Ce n'est pas la réponse. Attendez() fera exactement cela, attendez. Il n'annulera/n'annulera pas la tâche. –

0

Dan Je ne pense pas Task.Wait (délai d'attente) annule cette tâche, il y a surcharge Task.Wait (délai d'attente, cancelationToken), mais seulement sur les lancers francs OperationCanceledException task.Wait quand jeton est signalé.

Tâche. Ne bloque que lorsque la tâche est terminée ou que le délai expire, elle n'annule ni n'annule la tâche elle-même. Donc, la tâche bloquée restera suspendue dans ThreadPool. Vous ne pouvez pas éliminer une tâche non terminée (InvalidOperation).

Im ecrivais le même genre d'application que vous et j'ai écrit mon propre TaskScheduler qui permet Aborting (et n'utilise pas threadpool :().

Mais im très curieux de savoir comment vous avez résolu ce problème. S'il vous plaît répondre

+0

Après un examen plus approfondi, j'ai décidé que si un blocage se produisait, Thread.Abort ne fait que masquer tout problème sous-jacent, car l'état peut déjà avoir été corrompu. En tant que tel, je considère un échec de terminer pour être fatal (l'opérateur a l'option de continuer à attendre ou de mettre fin à l'application). C'est suffisant pour mon application car elle n'est pas critique de la mission; Si l'application était critique, je devais migrer vers AppDomains ou un processus d'hébergement distinct pour le code partiellement approuvé. –

+0

Dan, je pense que c'est la bonne façon d'y penser. Thread.Abort n'est définitivement pas une solution. Cela peut laisser votre application dans un état inconnu. –