You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何修改TestOption1通过子任务延续返回Task<Object>并优化任务调度

问题根源与解决方案

你遇到的核心问题不是OpenAsync没执行完成就跑后续代码,而是using语句的资源生命周期管理错误!当你在方法里return con.OpenAsync(...).ContinueWith(...)时,using块会在return语句执行后立刻释放SqlConnection和SqlCommand——但ContinueWith里的代码是在OpenAsync完成后才会执行的,这时候连接和命令已经被Dispose了,自然会抛出错误。

要实现用ContinueWith延续任务并返回带值的Task<T>,必须确保资源(连接、命令)在延续任务执行完成后再释放,不能用外层的using直接包裹。下面是具体的修正方案:

修正TestOption1(返回Task)

private static Task TestOption1(CancellationToken cancellationToken = default)
{
    var con = new SqlConnection(Membership);
    var cmd = con.CreateCommand();
    cmd.CommandType = System.Data.CommandType.Text;
    cmd.CommandText = "set nocount on";

    return con.OpenAsync(cancellationToken)
        .ContinueWith(t =>
        {
            try
            {
                cmd.ExecuteNonQuery();
            }
            finally
            {
                // 执行完后手动释放资源
                cmd.Dispose();
                con.Dispose();
            }
        }, cancellationToken, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default);
}

修正TestOptionReturn(返回Task)
private static Task<object> TestOptionReturn(CancellationToken cancellationToken = default)
{
    var con = new SqlConnection(Membership);
    var cmd = con.CreateCommand();
    cmd.CommandType = System.Data.CommandType.StoredProcedure;
    cmd.CommandText = "Test1";

    return con.OpenAsync(cancellationToken)
        .ContinueWith(t =>
        {
            object result = null;
            try
            {
                result = cmd.ExecuteScalar();
                return result;
            }
            finally
            {
                cmd.Dispose();
                con.Dispose();
            }
        }, cancellationToken, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default);
}

修正TestOptioDDL(返回Task,处理输出参数)
private static Task<object> TestOptioDDL(CancellationToken cancellationToken = default)
{
    var con = new SqlConnection(Membership);
    var cmd = con.CreateCommand();
    cmd.CommandType = System.Data.CommandType.StoredProcedure;
    cmd.CommandText = "TestOutput";
    var outputParam = new SqlParameter("Data", System.Data.SqlDbType.DateTime) 
    { 
        IsNullable = true, 
        Direction = System.Data.ParameterDirection.Output 
    };
    cmd.Parameters.Add(outputParam);

    return con.OpenAsync(cancellationToken)
        .ContinueWith(t =>
        {
            object result = null;
            try
            {
                cmd.ExecuteScalar();
                result = outputParam.Value;
                return result;
            }
            finally
            {
                cmd.Dispose();
                con.Dispose();
            }
        }, cancellationToken, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default);
}

修正TestOptionOutput(返回Task)

这里要注意:如果返回SqlDataReader,不能直接Dispose连接,因为SqlDataReader需要连接保持打开状态。这时候应该用CommandBehavior.CloseConnection,让阅读器关闭时自动释放连接:

private static Task<SqlDataReader> TestOptionOutput(CancellationToken cancellationToken = default)
{
    var con = new SqlConnection(Membership);
    var cmd = con.CreateCommand();
    cmd.CommandType = System.Data.CommandType.Text;
    cmd.CommandText = "select * from sys.databases";
    var outputParam = new SqlParameter("Data", System.Data.SqlDbType.DateTime) 
    { 
        IsNullable = true, 
        Direction = System.Data.ParameterDirection.Output 
    };
    cmd.Parameters.Add(outputParam);

    return con.OpenAsync(cancellationToken)
        .ContinueWith(t =>
        {
            try
            {
                // 使用CommandBehavior.CloseConnection,阅读器关闭时自动关闭并释放连接
                return cmd.ExecuteReader(CommandBehavior.CloseConnection);
            }
            catch
            {
                // 如果执行失败,手动释放资源
                cmd.Dispose();
                con.Dispose();
                throw;
            }
        }, cancellationToken, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default);
}

关键说明

  • 不再使用外层using,而是把资源的释放逻辑放到ContinueWith的finally块中(或者利用CommandBehavior自动处理),确保资源在任务执行完成后才被释放。
  • TaskContinuationOptions.ExecuteSynchronously可以让延续任务在OpenAsync完成的同一个线程上执行,减少线程切换开销,符合你最小化等待的需求。
  • 处理SqlDataReader时必须用CommandBehavior.CloseConnection,否则阅读器使用时连接已经被Dispose会报错。

内容的提问来源于stack exchange,提问作者Walter Verhoeven

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.07 10:22:45