6

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

我特别担心的是我安排了运行我不希望完全信任的代码的任务。特别是,我不能确定这个不受信任的代码不会死锁,因此我不能确定我使用这个代码安排的任务是否会完成。我想远离真正的 AppDomain 隔离(由于编组的开销和复杂性),但我也不想让 Task 线程悬而未决,陷入僵局。有没有办法在 TPL 中做到这一点?

4

3 回答 3

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

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

于 2010-05-06T06:43:16.830 回答
0

Dan 我不认为 Task.Wait(timeout) 会取消这个任务,有 Overload Task.Wait(timeout,cancelationToken),但是当令牌发出信号时,只会在 task.Wait 上抛出 OperationCanceledException。

Task.Wait 只会阻塞直到任务完成或超时到期,它不会取消或中止任务本身。所以死锁的任务会一直挂在线程池中。您不能处置未完成的任务 (InvalidOperation)。

我正在编写与您相同的应用程序,并且我确实编写了自己的 taskScheduler 允许中止(并且不使用线程池:()。

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

于 2010-05-03T16:18:34.143 回答
-4

您只需调用Task.Wait(timespanToWait).

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

于 2010-04-23T01:33:53.980 回答