从 C# AsyncCTP 使用 ExecuteReaderAsync 的任何缺点

问题描述 投票:0回答:3

有一些文章表明异步数据库调用在 .NET 中是个坏主意。

在 C# 异步 CTP 上,有一个名为

System.Data.SqlClient.SqlCommand
ExecuteReaderAsync
扩展。我对现有代码进行了如下一些操作:

var connectionString = System.Configuration.ConfigurationManager.ConnectionStrings["hubConnectionString"].ConnectionString;

using (var conn = new SqlConnection(connectionString)) {
    using (var cmd = new SqlCommand()) {

        cmd.Connection = conn;
        cmd.CommandText = "sp$DetailsTagsGetAllFromApprovedPropsWithCount";
        cmd.CommandType = System.Data.CommandType.StoredProcedure;

        conn.Open();

        var reader = cmd.ExecuteReader();
        while (reader.Read()) {

            //do the reading

        }

        conn.Close();
    }
}

我的代码中有几个这样的操作。所以,我正在考虑将它们转换为异步。

但另一方面,我并没有看到这种方法有多大吸引力(也许我没有看对正确的方向,谁知道呢!)。

那么,在这里使用这种新的异步编程模型有什么缺点吗?

编辑:

假设我重构代码如下:

public async Task<IEnumerable<Foo>> GetDataAsync() { 

    List<Foo> foos = new List<Foo>();

    var connectionString = System.Configuration.ConfigurationManager.ConnectionStrings["hubConnectionString"].ConnectionString;

    using (var conn = new SqlConnection(connectionString)) {
        using (var cmd = new SqlCommand()) {

            cmd.Connection = conn;
            cmd.CommandText = "sp$DetailsTagsGetAllFromApprovedPropsWithCount";
            cmd.CommandType = System.Data.CommandType.StoredProcedure;

            conn.Open();

            var reader = await cmd.ExecuteReaderAsync();
            while (reader.Read()) {

                //do the reading
                //create foos

            }

            conn.Close();
        }
    }

    return foos;

}

据我从await关键字的理解,它将其后面的代码转换为延续。此外,当它命中await关键字时,无论操作状态如何,它都会立即返回到其调用者。当它完成时,它会返回并触发继续代码。

这就是我的想法。

c# asp.net sql-server async-await
3个回答
65
投票

我不同意 Ricka 的观点。异步数据库命令不仅很好,而且对于实现规模、吞吐量延迟至关重要。他对线程池加速时间的反对意见仅适用于流量较低的 Web 服务器。

在高流量情况下(这是唯一重要的情况),线程池不必等待“注入”新线程。异步执行 SQL 命令不仅从 Web 服务器请求/线程运行状况的角度来看很重要,而且从总请求生命周期/延迟的角度来看也很重要:不相关的数据库调用可以并行执行,而不是顺序执行。仅此一项通常就会显着改善用户体验到的 HTTP 请求延迟。换句话说,您的页面加载速度更快。

忠告:在连接字符串上启用

Asynchronous Processing=true
之前,SQL 命令并不是真正的异步。虽然未设置此设置(默认情况下未设置,编辑:不再需要从 .NET Framework 开始< 4.5.
Asynchronous Processing
),但对
BeginExecuteReader
 的“异步”调用只不过是一个骗局,该调用将启动一个线程并阻止
that
线程。当在连接字符串中启用真正的异步处理时,调用是真正的异步,并且回调基于 IO 完成。 请注意:一旦 first 结果返回到客户端,异步 SQL 命令就会完成,并且信息消息将计为结果。

create procedure usp_DetailsTagsGetAllFromApprovedPropsWithCount as begin print 'Hello'; select complex query; end 您已经失去了异步的所有好处。

print
 创建一个发送回客户端的结果,该结果完成异步命令并在客户端上恢复执行并继续“reader.Read()”。现在 
that

将阻塞,直到复杂查询开始产生结果。你问

'谁把 
print 放入程序中?' 但是 print
 可能会伪装成其他东西,也许看起来像 
INSERT
一样无辜,执行
而不需要
首先发出
SET NOCOUNT ON .
    

我注意到以下问题没有得到解答:


0
投票
那么,在这里使用这种新的异步编程模型有什么缺点吗?

缺点

非常小

(小CPU/小内存),是因为在await语句之后运行的任何代码都可能在单独的线程上运行,因此存在状态机存储当前正在运行的线程的状态,以便可以在另一个线程上处理继续工作。更多关于 await/async 状态机的内容可以在 Dixin 博客 - 理解 C# async/await(一) 编译 雷姆斯写道:


0
投票

您已经失去了异步的所有好处。打印产生的结果是 被发送回客户端,客户端完成异步命令并 客户端上的执行恢复并继续 '读者.Read()'。现在它将阻塞,直到复杂的查询开始 产生结果。您问“谁在程序中打印?”但是 印刷品可能会伪装成其他东西,也许是 无辜的看起来像一个不首先发出一个执行的 INSERT 不设置计数。

现在我们已经到了.NET 8,这种情况似乎仍然存在。有解决方法吗?异步调用产生许多结果(包括通知客户端执行状态的消息)的存储过程的正确方法是什么?

例如:

declare @K int=0, @Progress varchar(255) while @K<100 begin set @K=@K+1 select Flush=1 set @Progress=concat('Executing ', @K, ' of 100') raiserror(@Progress, 9, 1) with nowait waitfor delay '00:00:01' end

马克。


© www.soinside.com 2019 - 2024. All rights reserved.