C# 有没有办法解决由第三方库引起的OS加载器锁死锁?
我有一个有趣的问题,我在其他任何地方都没有看到记录(至少不是这个特定的问题) 本期是COM、VB6和.NET的结合,使它们能够很好地发挥作用 以下是我所拥有的:C# 有没有办法解决由第三方库引起的OS加载器锁死锁?,c#,windows,multithreading,com,deadlock,C#,Windows,Multithreading,Com,Deadlock,我有一个有趣的问题,我在其他任何地方都没有看到记录(至少不是这个特定的问题) 本期是COM、VB6和.NET的结合,使它们能够很好地发挥作用 以下是我所拥有的: 旧式VB6 ActiveX DLL(由我们编写) 一种用C语言编写的多线程Windows服务,通过网络处理来自客户端的请求并发回结果。它通过创建一个新的STA线程来处理每个请求。每个请求处理程序线程实例化一个COM对象(在ActiveX DLL中定义)来处理请求并获得结果(传入一个XML字符串,然后返回一个XML字符串),显式释放CO
- 旧式VB6 ActiveX DLL(由我们编写)
- 一种用C语言编写的多线程Windows服务,通过网络处理来自客户端的请求并发回结果。它通过创建一个新的STA线程来处理每个请求。每个请求处理程序线程实例化一个COM对象(在ActiveX DLL中定义)来处理请求并获得结果(传入一个XML字符串,然后返回一个XML字符串),显式释放COM对象并退出。然后,服务将结果发送回客户端
- 所有网络代码都使用异步网络(即线程池线程)进行处理
-
正在启动的新线程正在创建一个新实例(VB6)COM对象来处理传入的请求。此时,COM运行时间处于调用对象类工厂的调用的中间。类工厂实现在VB6运行时本身(<强> MVBVM60.DLL<强>)。。也就是说,它调用VB6运行时的DllGetClassObject函数。这反过来调用内部运行时函数(
),它获取一个互斥体并进入一个关键部分来完成部分工作。此时,它将调用LoadLibrary将oleaut32.dll加载到进程中,同时保持该互斥体。因此,现在它保持该内部VB6运行时互斥体并等待OS加载程序锁定MSVBVM60!CThreadPool::InitRuntime
-
退出的线程已经在加载程序锁内部运行,因为它已经执行了托管代码,并在<强> Kelnel.ExtTyths/Stult>函数中执行。具体地,它在处理< <代码> DLLthTythyDeTea>代码>消息>强> >MVBVM60.DLL>强>线程上,这又调用这是一种在线程(
)上终止VB6运行时的方法。现在,此线程尝试获取另一个正在初始化的线程已经拥有的相同互斥体MSVBVM60!CThreadPool::terminaterUnitime
- 我不能从单个STA线程进行COM调用,因为这样服务将无法处理并发请求。我也不能让长时间运行的请求阻止其他客户端请求。这就是为什么我为每个请求创建一个STA线程
- 我需要在每个线程上创建COM对象的新实例,因为我需要确保每个实例在VB6代码中都有自己的全局变量副本(VB6为每个线程提供了自己的所有全局变量副本)
RPC_E_SERVERFAULT
错误。我假设这是因为COM marshalling和/或VB6运行时无法在ActiveX EXE中处理并发对象创建/销毁或并发方法调用
强制VB6代码在操作系统加载程序锁内运行
下,我切换到使用COM类的ActiveX DLL。为了强制VB6运行时在OS加载锁中运行线程初始化代码,我创建了一个原生(Win32)C++ DLL,在<强> dLMLIGN<强> >中处理代码< > DLL*THEADRADION/<代码> < <代码> DLL*THEADRADION/<代码>代码调用<强>
public class ClientHandler {
private static ManualResetEvent _safeForNewThread = new ManualResetEvent(true);
private void HandleRpcRequest(string request)
{
Thread rpcThread = new Thread(delegate()
{
DbRequestProxy dbRequest = null;
try
{
Thread.BeginThreadAffinity();
string response = null;
// Creates a COM object. The VB6 runtime initializes itself here.
// Other threads can be executing here at the same time without fear
// of a deadlock, because the VB6 runtime lock is re-entrant.
dbRequest = new DbRequestProxy();
// Call the COM object
response = dbRequest.ProcessDBRequest(request);
// Send response back to client
_messenger.Send(Messages.RpcResponse(response), true);
}
catch (Exception ex)
{
_messenger.Send(Messages.Error(ex.ToString()));
}
finally
{
if (dbRequest != null)
{
// Force release of COM objects and VB6 globals
// to prevent a different deadlock scenario with VB6
// and the .NET garbage collector/finalizer threads
dbRequest.Dispose();
}
// Other request threads cannot start right now, because
// we're exiting this thread, which will detach the VB6 runtime
// when the underlying native thread exits
_safeForNewThread.Reset();
Thread.EndThreadAffinity();
}
});
// Make sure we can start a new thread (i.e. another thread
// isn't in the middle of exiting...)
_safeForNewThread.WaitOne();
// Put the thread into an STA, start it up, and wait for
// it to end. If other requests come in, they'll get picked
// up by other thread-pool threads, so we won't usually be blocking anyone
// by doing this (although we are blocking a thread-pool thread, so
// hopefully we don't block for *too* long).
rpcThread.SetApartmentState(ApartmentState.STA);
rpcThread.Start();
rpcThread.Join();
// Since we've joined the thread, we know at this point
// that any DLL_THREAD_DETACH notifications have been handled
// and that the underlying native thread has completely terminated.
// Hence, other threads can safely be started.
_safeForNewThread.Set();
}
}