如何处置任务使用的托管资源?(不是.NET 4.5)
本文关键字:不是 NET 资源 任务 何处置 | 更新日期: 2023-09-27 18:00:24
我正在处理的特定场景涉及将SqlConnection和SqlCommand与任务结合使用。我想知道在任务完成时如何最好地处理SqlConnection和SqlCommand。假设我有这两个对象:
SqlConnection connection = new SqlConnection(base._ConnectionString);
SqlCommand command = new SqlCommand("SomeProcedure", connection);
我正在考虑两种不同的处理方式。
第一种是使用延续来清理所有内容。
return Task.Factory
.FromAsync<int>(
beginMethod: command.BeginExecuteNonQuery,
endMethod: command.EndExecuteNonQuery,
state: null)
.ContinueWith(a =>
{
if (command != null)
{
command.Dispose();
}
if (connection != null)
{
connection.Dispose();
}
// Need to propagate the exception too if one occurred.
a.Wait();
});
我担心的是,我认为这会引发另一个线程,只是为了处理延续?我使用FromAsync方法的全部原因是尽量避免这样做。此外,正如您在本例中看到的,我有点不确定如何最好地确保任何异常都能返回到调用者。我正在尝试这个技巧,等待强制继续任务重新抛出异常,但感觉不太对劲。
我正在研究的第二种方法使用lambda作为"endMethod"参数:
return Task.Factory
.FromAsync<int>(
beginMethod: command.BeginExecuteNonQuery,
endMethod: (asyncResult =>
{
int test = command.EndExecuteNonQuery(asyncResult);
command.Dispose();
connection.Dispose();
return test;
}),
state: null);
从表面上看,这似乎是一种更好的方法,尽管我不知道为什么会有这种感觉。我也觉得这不会引发第二条线索,但我对此并不完全确定。此外,在这种情况下,如果发生异常,我完全不知道如何将异常传播给调用者。事实上,我甚至不知道如何识别是否发生了异常。
提前感谢您的帮助。
如果不能使用async/await
,那么如果在多个Task.ContinueWith
回调中共享IDisposable
对象,那么确保它们得到正确处理可能会非常乏味。
为了实现这一点,您可以利用另一种编译器生成的状态机代码:IEnumerator
方法。这将允许具有伪线性代码流,并像往常一样使用try/finally
和using
语句。例如,以下内容基于Stephen Toub的Iterate
模式:
static IEnumerator<Task> GetSequenceOfTasks()
{
using (SqlConnection connection = new SqlConnection(base._ConnectionString))
{
yield return TestConnectionAsync(connection);
using (SqlCommand command = new SqlCommand("SomeProcedure", connection))
{
yield return ExecuteCommandAsync(command);
}
}
}
// ...
// task will complete when ExecuteCommandAsync has completed
var task = GetSequenceOfTasks().RunAsync();
// ...
// execute a sequence of tasks, propagate exceptions and cancellation (if any)
public static class TaskExt
{
public static Task<object> RunAsync(this IEnumerator<Task> @this)
{
if (@this == null)
throw new ArgumentNullException();
var tcs = new TaskCompletionSource<object>();
Action<Task> nextStep = null;
nextStep = (previousTask) =>
{
try
{
if (previousTask.IsCanceled)
tcs.SetCanceled();
else if (previousTask.IsFaulted)
tcs.SetException(previousTask.Exception);
else
{
// move to the next task
if (@this.MoveNext())
{
@this.Current.ContinueWith(nextStep,
TaskContinuationOptions.ExecuteSynchronously);
}
else
tcs.SetResult(previousTask);
}
}
catch (Exception ex)
{
tcs.TrySetException(ex);
}
};
nextStep(Task.FromResult(Type.Missing));
return tcs.Task.ContinueWith(
completedTask => { @this.Dispose(); return completedTask; },
TaskContinuationOptions.ExecuteSynchronously).Unwrap();
}
}