C# CancellationToken 如何与 SqlConnection.OpenAsync(token) 一起使用?
Posted
技术标签:
【中文标题】C# CancellationToken 如何与 SqlConnection.OpenAsync(token) 一起使用?【英文标题】:C# How is the CancellationToken used with SqlConnection.OpenAsync(token)? 【发布时间】:2019-06-06 14:40:29 【问题描述】:我正在尝试将 CancellationToken 与 SqlConnection.OpenAsync() 一起使用来限制 OpenAsync 函数所花费的时间。
我创建了一个新的 CancellationToken 并将其设置为在 200 毫秒后取消。然后我将它传递给 OpenAsync(token)。但是,此功能仍可能需要几秒钟才能运行。
查看文档我真的看不出我做错了什么。 https://docs.microsoft.com/en-us/dotnet/api/system.data.sqlclient.sqlconnection.openasync?view=netframework-4.7.2
这是我正在使用的代码:
private async void btnTestConnection_Click(object sender, EventArgs e)
SqlConnection connection = new SqlConnection(SQLConnectionString);
Task.Run(() => QuickConnectionTest(connection)).Wait();
public async Task QuickConnectionTest(SqlConnection connection)
CancellationTokenSource source = new CancellationTokenSource();
CancellationToken token = source.Token;
source.CancelAfter(200);
ConnectionOK = false;
try
using (connection)
await connection.OpenAsync(token);
if (connection.State == System.Data.ConnectionState.Open)
ConnectionOK = true;
catch (Exception ex)
ErrorMessage = ex.ToString();
当 CancellationToken 在 200 毫秒过去后抛出 OperationCanceledException 时,我希望 OpenAsync() 提前结束,但它只是等待。
为了复制这一点,我执行以下操作:
运行代码:结果 = 连接正常 停止 SQL 服务 运行代码:连接时长挂起。超时【问题讨论】:
你不是在OpenAsync
之前缺少一个await
吗?
referencesource.microsoft.com/#System.Data/System/Data/…
你能有你的真实代码吗?
SqlConnection
已经内置了超时机制。默认值为 15 秒,使用 Azure 时建议为 30 秒。无论如何,这使得设置亚秒级超时似乎有些不切实际。
如果我的帖子不清楚,请致歉。在找不到数据库的最坏情况下,我试图让超时时间小于 1 秒。 Connection.Timeout 不允许少于 1 秒,在测试时我发现测试连接可能需要 10 - 40 毫秒(在本地 PC 上)。
【参考方案1】:
CancelationToken
对其他人的作用与在您自己的代码中对您的作用相同。
示例:
class Program
static async Task Main(string[] args)
Console.WriteLine("main");
var cts = new CancellationTokenSource();
var task = SomethingAsync(cts.Token);
cts.Cancel();
await task;
Console.WriteLine("Complete");
Console.ReadKey();
static async Task SomethingAsync(CancellationToken token)
Console.WriteLine("Started");
while (!token.IsCancellationRequested)
await Task.Delay(2000); //didn't pass token here because we want to simulate some work.
Console.WriteLine("Canceled");
//**Outputs:**
//main
//Started
//… then ~2 seconds later <- this isn't output
//Canceled
//Complete
OpenAsync
方法可能需要一些优化,但不要指望调用Cancel()
会立即取消任何Task
。这只是一个编组标志,让Task
中的工作单元知道调用者想要取消它。 Task
中的工作可以选择取消的方式和时间。如果您设置该标志时它很忙,那么您只需要等待并相信Task
正在做它需要做的事情来结束事情。
【讨论】:
你说的都是真的,但这如何解决他的问题呢?他需要快速取消。 他需要取消并忘记。他不能强迫它停下来的速度比它要停下来的快。【参考方案2】:您的代码对我来说似乎是正确的。如果这没有按预期执行取消,则SqlConnection.OpenAsync
不支持可靠取消。取消是合作的。如果它没有得到适当的支持,或者存在错误,那么您无法保证。
尝试升级到最新的 .NET Framework 版本。您还可以尝试似乎领先于 .NET Framework 的 .NET Core。我关注了 GitHub 存储库,发现了多个与异步相关的 ADO.NET 错误。
您可以尝试在超时后拨打SqlConnection.Dispose()
。也许这行得通。您也可以尝试使用同步 API (Open
)。也许这使用了不同的代码路径。我相信不会,但值得一试。
如果这不起作用,您需要编写代码,以便即使连接任务尚未完成,您的逻辑也会继续进行。假装它已经完成,让它在后台徘徊,直到它自己完成。这可能会导致资源使用量增加,但可能没问题。
无论如何,请考虑在 GitHub corefx 存储库中打开一个问题,并使用最少的重现(例如连接到 example.com 的 5 行)。这应该可以工作。
【讨论】:
以上是关于C# CancellationToken 如何与 SqlConnection.OpenAsync(token) 一起使用?的主要内容,如果未能解决你的问题,请参考以下文章
C#源码生成器的cancellationtoken啥时候取消?
我应该何时调用 CancellationToken.ThrowIfCancellationRequested?