c# - SqlConnection.OpenAsync 问题
问题描述
我面临一个特殊的异步问题,我可以轻松重现但无法理解。
我当前的设置
我有一个 WCF 服务,它公开了两个 API——API1 和 API2。两个服务合同都是同步的。API1,在内存中查找字典,然后使用 Task.Factory.StartNew 创建一个任务以创建一个新任务,该任务从 SQL 服务器获取数据,将其与字典中的数据进行比较并写入一些日志。如果 SQl 服务器有连接问题,这将重试 SqlConnection.OpenAsync 3 次。请注意,API 调用本身一有字典中的数据就会返回(不等待 SQl 操作完成)
API2 要简单得多,它只是调用 SQL Server 上的存储过程,获取数据并返回。
打开连接的代码如下:
public static int OpenSqlConn(SqlConnection connection)
{
return OpenSqlConn(connection).Result;
}
public async static Task<int> OpenSqlConnAsync(SqlConnection connection)
{
return await OpenConnAsync(connection);
}
private static async Task<int> OpenConnAsync(SqlConnection connection)
{
int retryCounter = 0;
TimeSpan? waitTime = null;
while (true)
{
if (waitTime.HasValue)
{
await Task.Delay(waitTime.Value).ConfigureAwait(false);
}
try
{
startTime = DateTime.UtcNow;
await connection.OpenAsync().ConfigureAwait(false);
break;
}
catch (Exception e)
{
if (retryCounter >= 3)
{
SafeCloseConnection(connection);
return retryCounter;
}
retryCounter++;
waitTime = TimeSpan.FromSeconds(6);
}
}
return retryCounter;
}
API1 代码如下所示:
public API1Response API1 (API1Request request)
{
// look up in memory dictionary for the request
API1Response response = getDataFromDictionary(request);
// create a task to get some data from DB
Action action = () =>
{
GetDataFromDb(request);
}
Task.Factory.StartNew(action).ConfigureAwait(false);
// this is called immediately even if DB is not available and above task is retrying.
return API1Response;
}
public void GetDataFromDb(API1Request request)
{
using (var connection = new SqlConnection(...))
{
OpenSqlConn(connection);
/// hangs for long even if db is available
ReadDataFromDb(connection);
}
}
public API2Response API2(API2REquest request)
{
return GetDataFromDbForAPI2(request)
}
public API2Response GetDataFromDbForAPI2(API2Request request)
{
using (var connection = new SqlConnection(...))
{
OpenSqlConn(connection); /// hangs for long even if db is available
ReadDataFromDb(connection);
}
}
问题
当 SQL Server 短时间不可用时,该服务会遇到以下问题,并且某些客户端仅对 API1 进行了 100 次调用:
- 当我的 SQL 服务器出现连接问题并且我收到大约 100 次 API1 调用时,即使 API1 返回给调用者,它也会创建 100 个任务,这些任务将尝试打开与坏数据库的连接。这些任务中的每一个都在重试查找中挂起一段时间(这是预期的)。在我的实验中,我可以通过对 API1 使用错误的连接字符串来模拟数据库不可用。
- 现在假设数据库再次备份,并且对服务进行了对 API2 的调用。我发现当 API2 调用到达上面的 OpenAsync 部分时,它会挂起。只是挂起:(
一些观察 1. 当我查看 Visual Studio 中的“并行堆栈”时,我发现 API1 堆栈有 100 个线程执行以下堆栈:
ManualResetEvenSlim.Wait()
Task.SpinThenBlockingWait
Task.InternalWait();
Task<>.GetREsultCore
OpenConn()
API2 堆栈有 1 个线程,它再次位于与上述类似的堆栈中。
但是,如果我替换
SqlConnection.OpenAsync
为SqlConnection.Open()
,API2 调用会立即返回。
需要帮忙
我想了解的是为什么可以打开数据库连接(因为当时数据库可用)的API2也挂在OpenAsync上。我看到有任何明显的同步问题吗?当我将 SqlConnection.OpenAsync() 更改为 SqlConnection.Open() 时,为什么行为会发生变化?
解决方案
推荐阅读
- laravel-6.2 - Laratrust 多态用户模型返回错误的角色
- azure - Azcopy 使用内容类型从 S3 迁移内容
- kubernetes - 升级 web api 时避免重新创建 Kubernetes 集群
- mysql - Postgres Sql:需要将来自单个 JSON 输入参数的数据添加到 2 个不同的表中
- android - How do I use MVVM with bound service?
- javascript - ReactJS:如何在值中传递多个值?
- android - 什么是 kapt 异常?
- python - Python中基于括号的函数复合
- html - 试图居中- 这与clearfix有关吗?
- python-3.x - Python中的最大非连续子数组