此错误已开始偶尔且莫名其妙地发生,尤其是在连接到我们的 session 状态数据库时。这是错误:
Exception type: COMException
Exception message: The handle is invalid. (Exception from HRESULT: 0x80070006 (E_HANDLE))
at System.Runtime.InteropServices.Marshal.ThrowExceptionForHRInternal(Int32 errorCode, IntPtr errorInfo)
at System.Data.ProviderBase.DbConnectionPool.TryGetConnection(DbConnection owningObject, UInt32 waitForMultipleObjectsTimeout, Boolean allowCreate, Boolean onlyOneCheckConnection, DbConnectionOptions userOptions, DbConnectionInternal& connection)
at System.Data.ProviderBase.DbConnectionPool.TryGetConnection(DbConnection owningObject, TaskCompletionSource`1 retry, DbConnectionOptions userOptions, DbConnectionInternal& connection)
at System.Data.ProviderBase.DbConnectionFactory.TryGetConnection(DbConnection owningConnection, TaskCompletionSource`1 retry, DbConnectionOptions userOptions, DbConnectionInternal oldConnection, DbConnectionInternal& connection)
at System.Data.ProviderBase.DbConnectionInternal.TryOpenConnectionInternal(DbConnection outerConnection, DbConnectionFactory connectionFactory, TaskCompletionSource`1 retry, DbConnectionOptions userOptions)
at System.Data.SqlClient.SqlConnection.TryOpenInner(TaskCompletionSource`1 retry)
at System.Data.SqlClient.SqlConnection.TryOpen(TaskCompletionSource`1 retry)
at System.Data.SqlClient.SqlConnection.Open()
Windows 事件查看器中有时会出现可能相关的错误:
Application: w3wp.exe
Framework Version: v4.0.30319
Description: The process was terminated due to an unhandled exception.
Exception Info: System.Threading.SemaphoreFullException
Stack:
at System.Threading.Semaphore.Release(Int32)
at System.Data.ProviderBase.DbConnectionPool.CleanupCallback(System.Object)
at System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)
at System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)
at System.Threading.TimerQueueTimer.CallCallback()
at System.Threading.TimerQueueTimer.Fire()
at System.Threading.TimerQueue.FireNextTimers()
编辑:异常的另一种形式如下:
Exception Type: System.ComponentModel.Win32Exception
Error message: An operation was attempted on something that is not a socket
No Stack Trace Available
Exception Type: System.Data.SqlClient.SqlException
Error message: A transport-level error has occurred when sending the request to the server. (provider: TCP Provider, error: 0 - An operation was attempted on something that is not a socket.)
at System.Data.SqlClient.TdsParser.TdsExecuteRPC(_SqlRPC[] rpcArray, Int32 timeout, Boolean inSchema, SqlNotificationRequest notificationRequest, TdsParserStateObject stateObj, Boolean isCommandProc, Boolean sync, TaskCompletionSource`1 completion, Int32 startRpc, Int32 startParam)
at System.Data.SqlClient.SqlCommand.RunExecuteReaderTds(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, Boolean async, Int32 timeout, Task& task, Boolean asyncWrite, SqlDataReader ds)
at System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method, TaskCompletionSource`1 completion, Int32 timeout, Task& task, Boolean asyncWrite)
at System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method)
at System.Data.SqlClient.SqlCommand.ExecuteReader(CommandBehavior behavior, String method)
at System.Data.SqlClient.SqlCommand.ExecuteReader(CommandBehavior behavior)
at System.Web.SessionState.SqlSessionStateStore.SqlExecuteReaderWithRetry(SqlCommand cmd, CommandBehavior cmdBehavior)
Exception Type: System.Web.HttpException
Error message: Unable to connect to SQL Server session database.
at System.Web.SessionState.SqlSessionStateStore.SqlExecuteReaderWithRetry(SqlCommand cmd, CommandBehavior cmdBehavior)
at System.Web.SessionState.SqlSessionStateStore.DoGet(HttpContext context, String id, Boolean getExclusive, Boolean& locked, TimeSpan& lockAge, Object& lockId, SessionStateActions& actionFlags)
at System.Web.SessionState.SqlSessionStateStore.GetItem(HttpContext context, String id, Boolean& locked, TimeSpan& lockAge, Object& lockId, SessionStateActions& actionFlags)
at System.Web.SessionState.SessionStateModule.GetSessionStateItem()
at System.Web.SessionState.SessionStateModule.BeginAcquireState(Object source, EventArgs e, AsyncCallback cb, Object extraData)
at System.Web.HttpApplication.AsyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)
任何人都可以建议:
- 这是什么意思?
- 这可能是什么原因造成的(这是在一个已经运行了很长时间且非常稳定的应用程序中,在出现之前没有发生重大的基础架构变化)?
- 可以采取什么措施来解决它?
最佳答案
事实证明,我们将错误追踪到使用 Json.Net 反序列化 CancellationToken。
当代码仍在尝试使用已释放的操作系统句柄时,就会出现潜在问题。当然,当您的代码直接使用句柄时,可能会发生这种情况。我们的代码不会这样做,但事实证明,这可能会发生在 Json.Net 上。方法如下:
我们有一个类如下:
public class MyClass
{
...
}
// in one part of the code, this class was serialized & deserialized using Json.Net:
JsonConvert.SerializeObject(...);
JsonConvert.DeserializeObject<MyClass>(...);
当有人向 MyClass 添加类型为 CancellationToken 的属性时出现问题:
public class MyClass
{
...
public CancellationToken Token { get; set; }
}
问题来了。序列化后,CancellationToken 如下所示:
{"IsCancellationRequested":false,"CanBeCanceled":true,"WaitHandle":{"Handle":{"value":1508},"SafeWaitHandle":{"IsInvalid":false,"IsClosed":false}}}
请注意,这样做会延迟创建 token 的 WaitHandle 属性,并序列化其底层操作系统句柄 (1508) 的值。
当我们反序列化 token 时,Json.Net 将从 new CancellationToken()
(相当于 CancellationToken.None
)开始。然后它将继续使用保存的 IntPtr
值填充该 token 的 WaitHandle
的 Handle
属性。导致事情出错的一种明显方式是默认的 CancellationToken 的 WaitHandle 现在指向一个可能无效的句柄。然而,更大的问题是更新句柄会取消引用 WaitHandle 的原始 SafeHandle,从而允许垃圾收集器运行其终结器并清理它。然后,您可能会成为以下一组事件的受害者:
- 句柄 123 分配给池化数据库连接
- 反序列化将句柄 123 分配给默认取消 token 的 WaitHandle
- 第二次反序列化将新句柄值分配给默认取消标记的 WaitHandle
- 垃圾收集器运行并最终确定释放的 123 安全句柄值
- 数据库连接现在指向一个无效句柄
下面是一些使用 FileStream
故意复制问题的代码:
// serialize 2 tokens
var source = new CancellationTokenSource();
var serialized = JsonConvert.SerializeObject(source.Token);
var serialized2 = JsonConvert.SerializeObject(new CancellationTokenSource().Token);
var handle = source.Token.WaitHandle.Handle;
source.Dispose(); // releases source's handle
// spin until the OS gives us back that same handle as
// a file handle
FileStream fileStream;
while (true)
{
fileStream = new FileStream(Path.GetTempFileName(), FileMode.OpenOrCreate);
if (fileStream.Handle == handle) { break; }
}
// deserialize both tokens, thus releasing the conflicting handle
var deserialized = JsonConvert.DeserializeObject<CancellationToken>(serialized);
var deserialized2 = JsonConvert.DeserializeObject<CancellationToken>(serialized2);
GC.Collect();
GC.WaitForPendingFinalizers();
fileStream.WriteByte(1);
fileStream.Flush(); // fails with IOException "The handle is invalid"
关于c# - 打开SqlConnection "Handle is invalid"错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31168001/