在.NET 4 TPL中杀死一个死锁的任务

时间:2010-04-23 01:15:53

标签: .net deadlock parallel-processing task task-parallel-library

我想开始使用Task Parallel Library,因为这是执行异步操作的推荐框架。我无法找到的一件事是任何强制中止的方式,例如Thread.Abort提供的方法。

我特别关注的是我计划运行我不想完全信任的代码的任务。特别是,我不能确定这个不受信任的代码不会死锁,因此我无法确定使用此代码的任务我是否会完成。我想远离真正的AppDomain隔离(由于编组的开销和复杂性),但我也不想让任务线程挂起,死锁。有没有办法在TPL中做到这一点?

3 个答案:

答案 0 :(得分:9)

执行此操作的方法是使用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(或锁定)慢得多。

真的,如果你不相信代码的作者足以让他们实现合作取消,那么我会质疑让他们在同一个线程的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

答案 1 :(得分:0)

丹 我不认为Task.Wait(超时)将取消此任务,有Overload Task.Wait(timeout,cancelationToken),但只有在发出令牌信号时才会在task.Wait上抛出OperationCanceledException。

Task.Wait仅阻塞,直到任务完成或超时到期,它不会取消或中止任务本身。所以死锁的任务将在ThreadPool中保持不变。您无法处理UnCompleted Task(InvalidOperation)。

我正在编写与您相同类型的应用程序,并且我编写了自己的taskScheduler,它允许中止(并且不使用threadpool :()。

但我非常好奇你是如何解决这个问题的。请回复我。

答案 2 :(得分:-4)

您只需致电Task.Wait(timespanToWait)

如果在指定的时间跨度后任务未完成,则取消该任务。