1

在堆栈跟踪中MessageProcessor.Dispose()OnThreadShutdownRequestedRenewTaskLeaseCallback都需要一个锁来防止这些对象中的竞争条件。线程 (ID14968) 的堆栈跟踪保存所有锁,这会导致其他线程阻塞并等待它释放锁。

问题是,当CloseConnection()在 thread-14968 上调用时,连接永远不会关闭,而是SyncAsyncLock.Wait()在内部调用CloseConnection()。调用被阻塞,无法继续,出现死锁情况。

什么可能导致线程在CloseConnection()被调用时阻塞?

大图在这里死锁线程的堆栈跟踪

4

1 回答 1

0

在这个问题上工作了两天后,我终于弄清楚了问题所在。有一种情况可能导致应用程序出现死锁。

TaskLeaseRenewer对象实现IDisposable并且可以被多个线程访问。在类内部使用锁来确保没有两个线程会尝试同时释放对象。TaskLeaseRenewer有一个定时器和一个回调函数Callback,定时器将在一个单独的线程上定期调用。Callback线程将尝试调用Thread.Abort()创建TaskLeaseRenewer并最终调用Dispose()的线程TaskLeaseRenwer

当我尝试中止执行以下操作的线程时,就会出现问题:

using(TaskLeaseRenewer renewer = new TaskLeaseRewnewer())
{
    DoStuff();
}

我发现当你Thread.Abort()用语句调用线程时using,它不会终止线程,直到它调用Dispose()所用对象上的函数。

下面的示例将ConnectionWrapper.Dispose()在线程中止之前触发。

static void DisposeOnAbort()
{
    Thread t = new Thread(() =>
    {
        Console.WriteLine("Using connection wrapper");
        using (ConnectionWrapper wrapper = new ConnectionWrapper())
        {

            while (true)
            {
                Thread.Sleep(1000);
            }
        }
    });
    t.Start();
    Thread.Sleep(1000);
    Console.WriteLine("Aborting thread..");
    t.Abort();
}

鉴于这些,我发现问题是当Callback()调用时Thread.Abort()TaskLeaseRenewer.Dispose()将在它创建的线程上调用,因为线程正准备被杀死。但是Callback(),函数位于不同的线程上,并且还持有该Dispose()函数试图获取的锁。Dispose()将无法获得锁,并且线程永远不会终止。

解决了这个问题后,connection.Close()僵局似乎消失了。我仍然对可能阻止连接关闭的原因感兴趣。

在更多地解决这个问题之后,我发现当一个线程被中止时,一次性对象的Dispose()对象总是被调用。是否using使用语句。调用栈如下:

Threads.exe!Threads.ConnectionWrapper.Dispose() Line 150    C#
Threads.exe!Threads.Program.DisposeOnAbort.AnonymousMethod__0() Line 58 + 0x2c bytes    C#
于 2013-03-08T00:05:06.897 回答