我试图将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();
}
}
我希望OpenAsync()在200ms过去但取消等待时,如果CancellationToken抛出OperationCanceledException时提早结束。
要复制此代码,请执行以下操作:
答案 0 :(得分:0)
您的代码对我来说似乎是正确的。如果此操作未按预期执行取消操作,则SqlConnection.OpenAsync
不支持可靠的取消操作。取消是合作的。如果没有适当的支持,或者存在错误,那么您就无法保证。
尝试升级到最新的.NET Framework版本。您也可以尝试使用.NET Core,它似乎主导了.NET Framework。我遵循GitHub存储库,并看到了多个与异步相关的ADO.NET错误。
您可以尝试在超时后致电SqlConnection.Dispose()
。也许行得通。您也可以尝试使用同步API(Open
)。也许使用不同的代码路径。我相信不会,但是值得一试。
如果这两种方法都不起作用,那么即使连接任务尚未完成,您也需要编写代码以使逻辑继续进行。假装它已经完成,并使其在后台徘徊,直到其自身完成。这可能会导致资源使用增加,但这可能很好。
无论如何,请考虑以最少的复制(例如,连接到example.com的5条线)在GitHub corefx存储库中打开问题。这应该可以。
答案 1 :(得分:0)
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
正在完成包装工作即可。