代码之家  ›  专栏  ›  技术社区  ›  Dan Bryant

在.net 4 tpl中终止一个陷入僵局的任务

  •  6
  • Dan Bryant  · 技术社区  · 14 年前

    我想开始使用 Task Parallel Library ,因为这是执行异步操作的推荐框架。有一件事我找不到任何强制中止的方法,比如thread.abort提供了什么。

    我特别担心的是,我调度运行不希望完全信任的代码的任务。特别是,我不能确定这段不受信任的代码不会死锁,因此我不能确定我使用这段代码安排的任务是否会完成。我想远离真正的appdomain隔离(由于封送处理的开销和复杂性),但我也不想让任务线程死锁。有办法在第三方物流做到这一点吗?

    3 回复  |  直到 11 年前
        1
  •  9
  •   Ade Miller    14 年前

    方法是使用cancellationtoken和新的取消模型。 新的取消模型以几种类型集成到.NET框架中。最重要的是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;
            }
        }
    }
    

    第三方物流中的任务取消是协同的。换言之,这将始终死锁,因为由于任务线程被锁定,没有任何处理将取消令牌设置为已取消。

    这是有办法的,但它仍然依赖于不可信代码的作者来做正确的事情:

    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.wait handle获取一个句柄,并与其他同步原语的句柄一起等待。互斥比监视器(或锁)慢得多。

    实际上,如果您不信任代码的作者,不足以让他们实现协同取消,那么我会质疑让他们在同一线程上运行在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

        2
  •  0
  •   bosko    14 年前

    丹 我不认为task.wait(timeout)将取消此任务,存在重载task.wait(timeout,cancelationtoken),但这只会对task抛出operationcanceledexception。等待令牌发出信号。

    任务。仅等待块,直到任务完成或超时过期,它不会取消或中止任务本身。因此,陷入僵局的任务将一直悬而未决。无法释放未完成的任务(invalidooperation)。

    我正在编写与您和我自己编写的任务计划程序相同的应用程序,该程序允许中止(并且不使用threadpool:)

    但我很好奇你是怎么解决这个问题的。请回复我。

        3
  •  -4
  •   Foxfire    14 年前

    你只要打电话 Task.Wait(timespanToWait) .

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