如何处置任务使用的托管资源?(不是.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);

从表面上看,这似乎是一种更好的方法,尽管我不知道为什么会有这种感觉。我也觉得这不会引发第二条线索,但我对此并不完全确定。此外,在这种情况下,如果发生异常,我完全不知道如何将异常传播给调用者。事实上,我甚至不知道如何识别是否发生了异常。

提前感谢您的帮助。

如何处置任务使用的托管资源?(不是.NET 4.5)

如果不能使用async/await,那么如果在多个Task.ContinueWith回调中共享IDisposable对象,那么确保它们得到正确处理可能会非常乏味。

为了实现这一点,您可以利用另一种编译器生成的状态机代码:IEnumerator方法。这将允许具有伪线性代码流,并像往常一样使用try/finallyusing语句。例如,以下内容基于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();
    }
}
相关文章: