Каковы преимущества нативных *Async
методов, доступных в пространстве имен System.Data.SqlClient
? Каковы их преимущества перед руководством Task.Run
с телом, состоящим только из синхронных вызовов методов?
Вот мой пример «отправной точки» (консольное приложение):
using System;
using System.Data.SqlClient;
using System.Threading.Tasks;
class Program
{
const string CommandTest = @"
SET NOCOUNT ON;
WITH
L0 AS (SELECT c FROM (SELECT 1 UNION ALL SELECT 1) AS D(c)), -- 2^1
L1 AS (SELECT 1 AS c FROM L0 AS A CROSS JOIN L0 AS B), -- 2^2
L2 AS (SELECT 1 AS c FROM L1 AS A CROSS JOIN L1 AS B), -- 2^4
L3 AS (SELECT 1 AS c FROM L2 AS A CROSS JOIN L2 AS B), -- 2^8
L4 AS (SELECT 1 AS c FROM L3 AS A CROSS JOIN L3 AS B), -- 2^16
L5 AS (SELECT 1 AS c FROM L4 AS A CROSS JOIN L4 AS B), -- 2^32
Nums AS (SELECT ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS k FROM L5)
SELECT
k
FROM
Nums
WHERE
k <= 1000000";
const string ConnectionString = "Server=.;Database=master;Integrated Security=SSPI;";
// This requires c# 7.1 or later. Check project settings
public static async Task Main(string[] args)
{
var aSW = new System.Diagnostics.Stopwatch();
aSW.Restart();
{
var aRes = ExecuteSync();
Console.WriteLine($"ExecuteSync returned {aRes} in {aSW.Elapsed}.");
}
aSW.Restart();
{
var aRes = await ExecuteWrapperAsync();
Console.WriteLine($"ExecuteWrapperAsync returned {aRes} in {aSW.Elapsed}.");
}
aSW.Restart();
{
var aRes = await ExecuteNativeAsync();
Console.WriteLine($"ExecuteNativeAsync returned {aRes} in {aSW.Elapsed}.");
}
}
private static Task<long> ExecuteWrapperAsync()
{
return Task.Run(() => ExecuteSync());
}
private static long ExecuteSync()
{
using (var aConn = new SqlConnection(ConnectionString))
using (var aCmd = new SqlCommand(CommandTest, aConn))
{
aConn.Open();
using (var aR = aCmd.ExecuteReader())
{
long aRetVal = 0;
while (aR.Read())
aRetVal += aR.GetInt64(0);
return aRetVal;
}
}
}
private static async Task<long> ExecuteNativeAsync()
{
using (var aConn = new SqlConnection(ConnectionString))
using (var aCmd = new SqlCommand(CommandTest, aConn))
{
await aConn.OpenAsync();
using (var aR = await aCmd.ExecuteReaderAsync())
{
long aRetVal = 0;
while (await aR.ReadAsync())
aRetVal += aR.GetInt64(0);
return aRetVal;
}
}
}
}
Говоря о производительности моего механизма разработки, использование *Async
методов фактически привело к снижению времени работы. Как правило, мой вывод был следующим:
ExecuteSync returned 500000500000 in 00:00:00.4514950.
ExecuteWrapperAsync returned 500000500000 in 00:00:00.2525898.
ExecuteNativeAsync returned 500000500000 in 00:00:00.3662496.
Другими словами, метод ExecuteNativeAsync
- это метод, использующий *Async
методы System.Data.SqlClient
, и он чаще всего медленнее, чем синхронный метод, заключенный в вызов Task.Run
.
Я что-то не так делаю? Может быть, я неправильно читаю документацию?