替代解决方案:等待任务的句柄和手动重置事件
Task.WaitAny()
使用with Task
(由 SqlConnection.OpenAsync()' 返回)和作为参数接收并包装在Task
with中的手动重置事件时,我遇到了内存泄漏AsTask()
。这些对象没有被处理:) TaskCompletionSource<Object>, Task<Object>, StandardTaskContinuation, RegisteredWaitHandle, RegisteredWaithandleSafe, ContinuationResultTaskFromresultTask<Object,bool>, _ThreadPoolWaitOrTimerCallback
。
这是在 Windows 服务中使用的真实生产代码,该函数尝试在循环中打开与数据库的连接,直到打开连接,或操作失败,或ManualResetEvent _finishRequest
, 作为包含此代码的函数中的参数接收, 由任何其他线程中的代码发出信号。
为了避免泄漏,我决定反其道而行之:等待的句柄_finishRequest
和Task
返回的OpenAsync()
:
Task asyncOpening = sqlConnection.OpenAsync();
// Wait for the async open to finish, or until _finishRequest is signaled
var waitHandles = new WaitHandle[]
{
// index 0 in array: extract the AsyncWaitHandle from the Task
((IAsyncResult)asyncOpening).AsyncWaitHandle,
// index 1:
_finishRequest
};
// Check if finish was requested (index of signaled handle in the array = 1)
int whichFinished = WaitHandle.WaitAny(waitHandles);
finishRequested = whichFinished == 1;
// If so, break the loop to exit immediately
if (finishRequested)
break;
// If not, check if OpenAsync finished with error (it's a Task)
if (asyncOpening.IsFaulted)
{
// Extract the exception from the task, and throw it
// NOTE: adapt it to your case. In mine, I'm interested in the inner exception,
// but you can check the exception itself, for example to see if it was a timeout,
// if you specified it in the call to the async function that returns the Task
var ex = asyncOpening?.Exception?.InnerExceptions?[0];
if (ex != null) throw ex;
}
else
{
Log.Verbose("Connection to database {Database} on server {Server}", database, server);
break;
}
如果您还需要超时,您可以将其包含在对 的调用中OpenAsync
,或者您的异步函数中,然后检查异步操作的结果是否因为超时而被取消:完成时检查任务的状态,您可以请参阅代码注释中的注释。