Уничтожение заведенной в тупик Задачи в.NET 4 TPL

Я хотел бы начать пользоваться Библиотекой Параллели Задачи, поскольку это - рекомендуемая платформа, продвигающаяся для выполнения асинхронных операций. Одной вещью, которую я не смог найти, являются любые средства насильственного Аварийного прекращения работы, такой как какой Поток. Аварийное прекращение работы обеспечивает.

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

6
задан Dan Bryant 23 April 2010 в 01:15
поделиться

3 ответа

Это можно сделать с помощью CancellationToken и новой модели отмены. Новая модель отмены интегрирована в .NET Framework в нескольких типах. Наиболее важными из них являются System.Threading.Tasks, System.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 является кооперативной. Другими словами, это всегда будет взаимоблокировкой, потому что ничто не обрабатывает токен отмены, установленный на отмену, потому что поток задачи заблокирован.

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

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 намного медленнее, чем Monitor (или lock).

На самом деле, если вы недостаточно доверяете автору кода, чтобы он реализовал совместную отмену, я бы поставил под сомнение разумность их запуска внутри вашего 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

9
ответ дан 17 December 2019 в 04:44
поделиться

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

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

-4
ответ дан 17 December 2019 в 04:44
поделиться

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

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

Я пишу такое же приложение, как и вы, и я написал свой собственный TaskScheduler, который позволяет прервать выполнение (и не использует threadpool :( ).

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

0
ответ дан 17 December 2019 в 04:44
поделиться
Другие вопросы по тегам:

Похожие вопросы: