SqlConnection.OpenAsync问题

时间:2019-02-22 15:22:41

标签: c# sql-server async-await sqlconnection

我面临一个特殊的异步问题,我可以轻松地重现它,但无法理解。

我的当前设置

我有一个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. 当我的SQL Server存在连接问题时,即使API1返回到调用者,我也收到了大约100个API1调用,它创建了100个任务,这些任务将尝试打开与错误DB的连接。这些任务中的每一个都会在重试外观中挂起一段时间(这是预期的)。在我的实验中,我可以通过为API1使用错误的连接字符串来模拟数据库不可用。
  2. 现在,假设数据库再次备份,并且对该服务进行了API2调用。我发现,当API2调用到达上面的OpenAsync部分时,它挂起了。 只是挂起:(

一些观察 1.当我查看Visual Studio中的“并行堆栈”时,发现有100个线程与API1堆栈进行以下堆栈:

 ManualResetEvenSlim.Wait()
 Task.SpinThenBlockingWait
 Task.InternalWait();
 Task<>.GetREsultCore
 OpenConn()
  1. API2堆栈有1个线程,该线程也与上述类似。

  2. 但是,如果我将SqlConnection.OpenAsync替换为SqlConnection.Open(),API2调用会立即返回。

需要帮助

我想了解的是为什么可以打开DB连接(因为当时可以使用DB)的API2仍然挂在OpenAsync上。我看到任何明显的同步问题吗?当我将SqlConnection.OpenAsync()更改为SqlConnection.Open()时,行为为何改变?

0 个答案:

没有答案