6

Я хотел бы начать использовать Task Parallel Library, так как это рекомендуемая структура для выполнения асинхронных операций. Одна вещь, которую я не смог найти, - это любые средства принудительного прерывания, такие как Thread.Abort.Убив тупиковую задачу в .NET 4 TPL

Моя особая забота заключается в том, что я планирую выполнение заданий с кодом, который я не хочу полностью доверять. В частности, я не могу быть уверен, что этот ненадежный код не будет блокироваться, и поэтому я не могу быть уверенным, что задание, которое я планирую использовать, будет когда-либо завершено. Я хочу держаться подальше от истинной изоляции AppDomain (из-за накладных расходов и сложности маршалинга), но я также не хочу оставлять цепочку задач зависающей, тупиковой. Есть ли способ сделать это в TPL?

ответ

9

Способ отмены: отменить отмену и отмену. Новая модель отмены интегрирована в .NET Framework в несколько типов. .Threading.Tasks.Task, System.Threading.Tasks.Task и System.Linq.ParallelEnumerable.

Вот пример вашей проблемы. Этот код всегда будет тупиковым, потому что вызывающий код сначала блокирует блокировку, а затем тупиковая задача пытается получить тот же замок.

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; 
     } 
    } 
} 

Отмена задачи в TPL является совместной. Другими словами, это всегда будет deadlock, потому что ничего не обрабатывает токен отмены, который отменяется, потому что поток задачи заблокирован.

Существует способ обойти это, но он по-прежнему полагается на авторов ненадежного кода, чтобы сделать правильную вещь:

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; 
    } 
} 

Очки, чтобы отметить; аннулирование - сотрудничество. Вы можете использовать Token.WaitHandle, чтобы получить дескриптор и ждать его вместе с дескрипторами других примитивов синхронизации. Mutex намного медленнее, чем монитор (или блокировка).

Действительно, если вы не доверяете автору кода достаточно, чтобы они реализовали совместную отмену, я бы поставил под сомнение здравый смысл того, что они запускаются внутри вашего AppDomain в той же теме.

Для более подробно см:

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

Спасибо, это полезная информация. Тем не менее, у меня создалось впечатление, что Задача должна прослушивать запрос на аннулирование и вызывать OperationCancelledException самостоятельно, если он не может полностью выполнить. Я попробую ваш код, когда я получу шанс сегодня днем. –

+0

Из первой ссылки «Слушатели могут быть уведомлены об аннулировании запросов путем опроса, регистрации обратного вызова или ожидания ожидающих дескрипторов». Настолько эффективно Task.Wait вызывает прослушивание. –

+0

См. Http://stackoverflow.com/questions/2293976/how-and-if-to-write-a-single-consumer-queue-using-the-task-parallel-library/2779208#2779208 для примера используя IsCancellationRequested для проверки отмены и ответа на него. –

-4

Вы просто позвоните Task.Wait(timespanToWait).

Если задание не завершено после указанного периода времени, оно отменяется.

+0

Спасибо. Это не совсем очевидно из документации, но имеет смысл, когда вы думаете о задачах как изолирующих вас от любых подробностей о том, как задачи планируются и управляются. Знаете ли вы, что мне нужно будет сделать что-нибудь особенное после того, как Wait вернется, прежде чем я смогу безопасно удалить эту задачу? –

+11

Это не ответ. Wait() сделает именно это, подождите. Он не отменяет/отменяет задачу. –

0

Dan Я не думаю, что Task.Wait (timeout) отменит эту задачу, будет перегрузка Task.Wait (timeout, cancelationToken), но это только бросает OperationCanceledException на task.Wait, когда сигнализируется токен.

Задача. Подождите, пока не завершится выполнение любой задачи или не истечет время ожидания, она не отменяет и не отменяет задачу. Таким образом, тупиковая задача останется висящей в ThreadPool. Вы не можете уничтожить незавершенную задачу (InvalidOperation).

Im сочинительства такой же приложения, как вы и я написал свой собственный TaskScheduler, который позволяет Прерывание (и не используя ThreadPool :().

Но им очень интересно, как вы решили эту проблему. Пожалуйста, ответьте для меня

+0

При дальнейшем рассмотрении я решил, что если произойдет взаимоблокировка, Thread.Abort действительно только маскирует любую основную проблему, поскольку состояние, возможно, уже было повреждено. Таким образом, я рассматриваю возможность отказа от фатальной смерти (оператор имеет возможность продолжать или прекращать действие приложения). Этого достаточно для моего приложения, поскольку оно не является критическим для миссии; если приложение было критически важным, мне пришлось бы перейти на использование AppDomains или отдельный процесс хостинга для частично доверенного кода. –

+0

Дэн, я думаю, что это правильный способ подумать об этом. Thread.Abort определенно не является решением. Он может оставить ваше приложение в неизвестном состоянии. –

Смежные вопросы