gpt4 book ai didi

c# - 如何使 SqlDataReader.ReadAsync() 异步运行?

转载 作者:太空狗 更新时间:2023-10-29 23:49:29 27 4
gpt4 key购买 nike

当调用 SQL Server 执行实际需要时间的操作时,SqlDataReader.ReadAsync() 会为我同步运行。有没有办法强制它异步运行,或者是我在 Task.Run() 中调用它的唯一选择?

这是一个重现。它使用 winforms 来演示调用阻塞了 GUI 线程。请注意,T-SQL 必须实际执行某些操作 —这无法通过 WAITFOR DELAY '00:00:20' 重现。

using System;
using System.Configuration;
using System.Data.Common;
using System.Data.SqlClient;
using System.Threading.Tasks;
using System.Windows.Forms;

static class SqlDataReaderReadAsyncProgram
{
static async void Form_Shown(object sender, EventArgs e)
{
var form = (Form)sender;
// Declare your connection string in app.config like
// <connectionStrings><remove name="LocalSqlServer"/><add name="LocalSqlServer" connectionString="Data Source=localhost\SQLEXPRESS;Integrated Security=true"/></connectionStrings>
using (DbConnection connection = new SqlConnection(ConfigurationManager.ConnectionStrings[0].ConnectionString))
{
form.Text = "connecting…";
await connection.OpenAsync();
form.Text = "connected!";
// Install a stored procedure.
using (var command = connection.CreateCommand())
{
command.CommandText = "SET NOCOUNT ON"
+ " SELECT 'a'"
+ " DECLARE @t DATETIME = SYSDATETIME()"
+ " WHILE DATEDIFF(s, @t, SYSDATETIME()) < 20 BEGIN"
+ " SELECT 2 x INTO #y"
+ " DROP TABLE #y"
+ " END"
+ " SELECT 'b'";
form.Text = "executing…";
using (var reader = await command.ExecuteReaderAsync())
{
form.Text = "reading…";
do
{
// Blocks on the second call until the second resultset is returned by SQL Server
while (await reader.ReadAsync())
{
}
} while (await reader.NextResultAsync());
form.Text = "done!";
}
}
}
await Task.Delay(TimeSpan.FromSeconds(5));
form.Close();
}

[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
var form = new Form();
form.Shown += Form_Shown;
Application.Run(form);
}
}

当我运行它时,窗口在报告完成之前变为“(无响应)”20 秒(请注意,在 VS 中调试时,“(无响应)”文本不会出现,但它仍然卡住相同) .如果我在 VS 中调试并在它卡住时中断它,我会看到它的调用堆栈如下所示:

    [Managed to Native Transition]  
System.Data.dll!SNINativeMethodWrapper.SNIReadSyncOverAsync(System.Runtime.InteropServices.SafeHandle pConn, ref System.IntPtr packet, int timeout) Unknown
System.Data.dll!System.Data.SqlClient.TdsParserStateObject.ReadSniSyncOverAsync() Unknown
System.Data.dll!System.Data.SqlClient.TdsParserStateObject.TryReadNetworkPacket() Unknown
System.Data.dll!System.Data.SqlClient.TdsParserStateObject.TryPrepareBuffer() Unknown
System.Data.dll!System.Data.SqlClient.TdsParserStateObject.TryReadByteArray(byte[] buff, int offset, int len, out int totalRead) Unknown
System.Data.dll!System.Data.SqlClient.TdsParserStateObject.TryReadInt64(out long value) Unknown
System.Data.dll!System.Data.SqlClient.TdsParser.TryProcessDone(System.Data.SqlClient.SqlCommand cmd, System.Data.SqlClient.SqlDataReader reader, ref System.Data.SqlClient.RunBehavior run, System.Data.SqlClient.TdsParserStateObject stateObj) Unknown
System.Data.dll!System.Data.SqlClient.TdsParser.TryRun(System.Data.SqlClient.RunBehavior runBehavior, System.Data.SqlClient.SqlCommand cmdHandler, System.Data.SqlClient.SqlDataReader dataStream, System.Data.SqlClient.BulkCopySimpleResultSet bulkCopyHandler, System.Data.SqlClient.TdsParserStateObject stateObj, out bool dataReady) Unknown
System.Data.dll!System.Data.SqlClient.SqlDataReader.TryHasMoreRows(out bool moreRows) Unknown
System.Data.dll!System.Data.SqlClient.SqlDataReader.TryReadInternal(bool setTimeout, out bool more) Unknown
System.Data.dll!System.Data.SqlClient.SqlDataReader.ReadAsync.AnonymousMethod__0(System.Threading.Tasks.Task t) Unknown
System.Data.dll!System.Data.SqlClient.SqlDataReader.InvokeRetryable<bool>(System.Func<System.Threading.Tasks.Task, System.Threading.Tasks.Task<bool>> moreFunc, System.Threading.Tasks.TaskCompletionSource<bool> source, System.IDisposable objectToDispose) Unknown
System.Data.dll!System.Data.SqlClient.SqlDataReader.ReadAsync(System.Threading.CancellationToken cancellationToken) Unknown
System.Data.dll!System.Data.Common.DbDataReader.ReadAsync() Unknown
> SqlDataReaderReadAsync.exe!SqlDataReaderReadAsyncProgram.Form_Shown(object sender, System.EventArgs e) Line 36 C#
[Resuming Async Method]

(为简洁起见进一步修剪)。

整个 ReadSyncOverAsync 东西在我看来特别可疑。这就像 SqlClient 假设同步读取不会阻塞,就好像它不知道如何使用非阻塞 IO 或其他东西一样。然而,当查看引用源或使用 JustDecompile 反编译时,看起来应该有异步支持,但它只是以某种方式启发式/后备地决定不使用它。

那么,我如何让 SqlClient 中的 *Async() 东西真正成为异步的呢?我认为这些方法应该使我能够编写无线程的响应式 GUI 程序而无需使用 Task.Run() 因为将同步内容包装在 Task.Run() 仅仅让它们异步是毫无意义的开销……?

我正在使用 .net-4.7.02542。

我假设这是一个 .net 错误并已提交 connect #3139210 (编辑:连接已死,我在 https://github.com/binki/connect3139210 有一个重现项目)。

更新:Microsoft 承认该错误并将在 .net-4.7.3 中修复它。 我使用了来自 VS 订阅的“技术支持”案例来报告错误并获取此信息。

最佳答案

Microsoft 在 .net-4.8 中针对此问题发布了修复程序。我已经测试并验证它有效。我还没有看到 .net-4.7.3 的版本,所以我不知道它是否真的包含修复程序。

regedit 中 releaseKey=528040 的相关 SKU:

regedit showing <code>.NETFramework,Version=v4.7.2</code> followed by <code>.NETFramework,Version=v4.8</code>, demonstrating that <code>.NETFramework,Version=4.7.3</code> is absent

您的应用程序必须以 .net-4.8 为目标才能获得修复(仅安装更新不会修复已编译的应用程序)。不幸的是,没有记录 <AppContextSwitchOverrides/> 对于此功能,如果您必须继续以旧版本的 .net 为目标,则不能选择加入修复程序。 (但是,您可以在编译时以 .net-4.8 为目标,编辑 «ProgramName».config 以更改 <supportedRuntime/> ,然后注意不要在您的目标版本之后使用 .net 中引入的任何 API)。

关于c# - 如何使 SqlDataReader.ReadAsync() 异步运行?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45597995/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com