gpt4 book ai didi

c# - 使用 while 循环取消查询永远挂起

转载 作者:太空宇宙 更新时间:2023-11-03 12:18:45 24 4
gpt4 key购买 nike

我正在尝试使用查询取消(通过取消 token )来取消长时间运行的复杂查询。我发现在某些情况下,取消不仅无法停止查询,而且对 CancellationToken.Cancel() 的调用也会无限期挂起。这是一个复制此行为的简单重现(可以在 LinqPad 中运行):

void Main()
{
var cancellationTokenSource = new CancellationTokenSource();
var blocked = RunSqlAsync(cancellationTokenSource.Token);
blocked.Wait(TimeSpan.FromSeconds(1)).Dump(); // false (blocked in SQL as expected)
cancellationTokenSource.Cancel(); // hangs forever?!
Console.WriteLine("Finished calling Cancel()");
blocked.Wait();
}

public async Task RunSqlAsync(CancellationToken cancellationToken)
{
var connectionString = new SqlConnectionStringBuilder { DataSource = @".\sqlexpress", IntegratedSecurity = true, Pooling = false }.ConnectionString;
using (var connection = new SqlConnection(connectionString))
{
await connection.OpenAsync().ConfigureAwait(false);

using (var command = connection.CreateCommand())
{
command.CommandText = @"
WHILE 1 = 1
BEGIN
DECLARE @x INT = 1
END
";
command.CommandTimeout = 0;
Console.WriteLine("Running query");
await command.ExecuteNonQueryAsync(cancellationToken).ConfigureAwait(false);
}
}
}

有趣的是,在 SqlServer Management Studio 中运行的相同查询会通过“取消执行查询”按钮立即取消。

在无法取消紧凑的 WHILE 循环的情况下查询取消是否有一些注意事项?

我的 SqlServer 版本:

Microsoft SQL Server 2012 - 11.0.2100.60 (X64) Feb 10 2012 19:39:15 Copyright (c) Microsoft Corporation Express Edition (64-bit) on Windows NT 6.2 (Build 9200: )

我在 Windows 10 上运行,.NET 的 Environment.Version 是 4.0.30319.42000。

编辑

一些附加信息:

这是 cancellationToken.Cancel() 挂起时从 Visual Studio 中提取的堆栈跟踪:

Cancel() stack trace

另一个线程卡在这里:

ExecuteNonQueryAsync() stack trace

此外,我尝试更新到 SqlServer Express 2017,我看到了相同的行为。

编辑

我已将此作为 corefx 的错误归档:https://github.com/dotnet/corefx/issues/26623

最佳答案

我可以在控制台应用程序中重现该问题。 (问题中的代码是来自 LINQPad 的代码。)

我要把它作为一个答案,并说这是 ADO.NET 中的一个错误。 ADO.NET 应该向 SQL Server 发送查询取消信号。从 CPU 使用率可以看出,SQL Server 继续执行循环。因此,它没有收到客户的取消通知。我们还知道 SSMS 能够取消此循环。

当循环运行时,我可以看到控制台应用程序正在使用一个 CPU 内核的 50%,并以 70MB/秒的速度从 SQL Server 接收数据。我不知道这是什么数据。它可能是 ROWCOUNT 信息或相关信息。

我认为该错误与以下事实有关:循环不断发送数据,因此 ADO.NET 永远没有机会发送取消。它仍然是一个错误,如果您报告它,它将成为一项社区服务。您可以链接到这个问题。

如果使用...限制循环

            WHILE 1 = 1
BEGIN
DECLARE @x INT = 1
WAITFOR DELAY '00:00:01' --new
END

...然后取消很快。

此外,您通常不能指望取消很快。如果网络断开,客户端可能需要 30 秒才能注意到并抛出。

因此,您需要对程序进行编码,使其继续执行而不是等待查询完成。它可能看起来像这样:

var queryTask = ...;
var cancellationToken = ...;

await Task.WhenAll(queryTask, cancellationToken);

这样取消总是看起来瞬间。确保资源仍处于处置状态。所有 SQL 交互都应封装在 queryTask 中,以便它在后台继续并最终清理。

关于c# - 使用 while 循环取消查询永远挂起,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48461567/

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