SqlConnection.Open 与 SqlConnection.OpenAsync - 两者之间除了明显的区别之外还有什么不同?

问题描述 投票:0回答:2

编辑: 这归结为为什么在异步代码中仅将 SqlConnection.Open() 更改为等待 SqlConnection.OpenAsync() 会导致截然不同的行为。

除了明显的异步行为之外,同步代码中的 SqlConnection.Open 调用和异步代码中的等待 SqlConnection.OpenAsync 调用之间还有什么区别?底层连接是否与数据库异步?

有关 OpenAsync 的文档很精简,https://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlconnection.openasync%28v=vs.110%29.aspx?f=255&MSPPError=- 2147217396.

Open 的异步版本,用于打开数据库连接 使用 ConnectionString 指定的设置。这个方法 调用虚拟方法 OpenAsync CancellationToken.None。(继承自DbConnection。)

我发现有趣的是,以前连接字符串需要 async=true ,而在 .net 4.5+ 中不再需要。连接的行为是否有所不同?

https://msdn.microsoft.com/en-us/library/hh211418(v=vs.110).aspx

从 .NET Framework 4.5 开始,这些方法不再需要 连接字符串中的异步处理=true。

当我碰巧在异步应用程序中使用同步 SqlConnection.Open 并大量加载它时,我发现它的性能非常差,连接池很早就干涸了。我预计打开连接会被阻塞,但是,在这些连接上执行异步命令(通过 dapper)的行为有所不同。那么,OpenAsync 有何不同之处?

编辑:

按照要求的代码来重现问题(或者可能展示差异)。使用 Open() 运行此案例时,在执行大约 180 个并发异步命令时会遇到连接超时,而使用 OpenAsync() 即使在执行超过 300 个并发命令时也不会遇到异常。您可以推动并发性以最终使其超时,但它肯定会更深入地执行并发命令。

using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Dapper;
using Nito.AsyncEx;

namespace AsyncSqlConnectionTest
{
    class Program
    {
        public static int concurrent_counter = 0;
        public static int total_counter = 0;

        static void Main(string[] args)
        {


            var listToConsume = Enumerable.Range(1, 10000).ToList();
            Parallel.ForEach(listToConsume,
                new ParallelOptions { },
                value =>
                {
                    try
                    {

                        Task.Run(() => AsyncContext.Run(async () =>
                        {
                            using (var conn = new SqlConnection("Data Source=.; Database=master; Trusted_Connection=True;"))
                            {
                                Interlocked.Increment(ref concurrent_counter);
                                Interlocked.Increment(ref total_counter);
                                await conn.OpenAsync();
                                var result = await conn.QueryAsync("select * from master..spt_values; waitfor delay '00:00:05'");
                                Console.WriteLine($"#{total_counter}, concurrent: {concurrent_counter}");
                                Interlocked.Decrement(ref concurrent_counter);
                            }
                        })).GetAwaiter().GetResult();
                    }
                    catch (Exception e)
                    {
                        Console.Write(e.ToString());
                    }
                });
            Console.ReadLine();
        }
    }
}

编辑2:

下面的测试只使用 ADO.NET 就发现了相同的差异。值得注意的是,Dapper 的执行速度要快得多,但这不是重点。同样,OpenAsync 最终会超时,但会“晚一些”,如果最大并行度为 100(低于连接池大小),则永远不会超时。

using System;
using System.Data.SqlClient;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace AsyncSqlConnectionTest
{
    class Program
    {
        public static int concurrent_counter = 0;
        public static int total_counter = 0;

        static void Main(string[] args)
        {
            var listToConsume = Enumerable.Range(1, 10000).ToList();
            Parallel.ForEach(listToConsume,
                new ParallelOptions { },
                value =>
                {
                    try
                    {

                        Task.Run(async () =>
                        {
                            using (var conn = new SqlConnection("Data Source=.; Database=master; Trusted_Connection=True;"))
                            {
                                Interlocked.Increment(ref concurrent_counter);
                                Interlocked.Increment(ref total_counter);

                                // this (no errors)
                                await conn.OpenAsync();

                                // vs. this (timeouts)
                                //conn.Open();

                                var cmd = new SqlCommand("select * from master..spt_values; waitfor delay '00:00:05'", conn);
                                using (var reader = await cmd.ExecuteReaderAsync())
                                {
                                    while (await reader.ReadAsync()) { }
                                }
                                Console.WriteLine($"#{total_counter}, concurrent: {concurrent_counter}");
                                Interlocked.Decrement(ref concurrent_counter);
                            }
                        }).GetAwaiter().GetResult();
                    }
                    catch (Exception e)
                    {
                        Console.Write(e.ToString());
                    }
                });
            Console.ReadLine();
        }
    }
}
c# asynchronous ado.net
2个回答
4
投票

Open() 是一个同步进程,它会冻结 UI,而 OpenAsync() 是一个异步进程,它会在不冻结 UI 的情况下打开连接

    public static async Task<SqlConnection> GetConnectionAsync()   
{  
      var con = new SqlConnection(ConnectionString);   
      if (con.State != ConnectionState.Open)   
          await con.OpenAsync();  
      return con;  
}  
  
public async Task<int> ExecuteQueryAsync(SqlConnection con, string query)  
{  
      if (con == null) con = await GetConnectionAsync();   
      var cmd = new SqlCommand(query, con);   
      return await cmd.ExecuteNonQueryAsync();   
}  

0
投票

查看新

SqlConnection
中的 Microsoft.Data.SqlClient 源代码
,看起来 
OpenAsync
 是同步工作的。它只是异步实现重试逻辑,并在同步重载周围添加取消令牌支持(ish),就是这样。

© www.soinside.com 2019 - 2024. All rights reserved.