我面临一个特殊的异步问题,我可以轻松地重现它,但无法理解。
我的当前设置
我有一个WCF服务,它公开了两个API-API1和API2。两个服务合同都是同步的。 API1,在内存中查找字典,然后使用Task.Factory.StartNew创建任务以创建一个新任务,该任务从SQL Server提取数据,将其与字典中的数据进行比较,并写入一些日志。如果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次调用时,该服务也会遇到以下问题:
一些观察 1.当我查看Visual Studio中的“并行堆栈”时,发现有100个线程与API1堆栈进行以下堆栈:
ManualResetEvenSlim.Wait()
Task.SpinThenBlockingWait
Task.InternalWait();
Task<>.GetREsultCore
OpenConn()
API2堆栈有1个线程,该线程也与上述类似。
但是,如果我将SqlConnection.OpenAsync
替换为SqlConnection.Open()
,API2调用会立即返回。
需要帮助
我想了解的是为什么可以打开DB连接(因为当时可以使用DB)的API2仍然挂在OpenAsync上。我看到任何明显的同步问题吗?当我将SqlConnection.OpenAsync()更改为SqlConnection.Open()时,行为为何改变?