C#CancellationToken如何与SqlConnection.OpenAsync(token)一起使用?

时间:2019-01-11 16:30:27

标签: c# asynchronous

我试图将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时提早结束。

要复制此代码,请执行以下操作:

  • 运行代码:结果=连接正常
  • 停止SQL服务
  • 运行代码:挂起连接长度。超时

  • 2 个答案:

    答案 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正在完成包装工作即可。