如何使两个SQL查询真正异步

Ler*_*ica 18 c# ado.net multithreading task-parallel-library async-await

我的问题是基于一个真正的项目问题,但我从未使用过该System.Threading.Tasks库或执行任何涉及线程的严肃编程,因此我的问题可能是缺乏对特定库的了解以及对异步实际意味着什么的更普遍的误解.节目.

所以我的真实情况就是这个 - 我需要获取有关用户的数据.在我目前的情况下,它是财务数据,所以假设我需要所有人Accounts,所有人Deposits和所有人Consignations.在我的情况下,这意味着查询每个属性的数百万条记录,并且每个查询本身相对较慢,但是获取它Accounts比获取数据慢几倍Deposits.所以我已经为我要使用的三种银行产品定义了三个类,当我想获取某些用户的所有银行产品的数据时,我做了类似这样的事情:

List<Account> accounts = GetAccountsForClient(int clientId);
List<Deposit> deposits = GetDepositsForClient(int clientId);
List<Consignation> consignations = GetConsignationsForClient(int clientId);
Run Code Online (Sandbox Code Playgroud)

所以问题从这里开始我需要同时获取所有这三个列表,因为我要将它们传递给我显示所有用户数据的视图.但正如现在一样,执行是同步的(如果我在这里正确使用这个术语),那么收集所有三种产品的数据的总时间是:

Total_Time = Time_To_Get_Accounts + Time_To_Get_Deposits + Time_To_Get_Consignations
Run Code Online (Sandbox Code Playgroud)

这不好,因为每个查询都比较慢,所以总时间很长,但是,accounts查询比其他两个查询花费的时间要多得多,所以今天进入我脑海的想法是 - "如果我能执行怎么办?这同时查询".也许这是我对这个主题的最大误解,但对我而言,最接近这个想法是让它们异步,所以也许Total_Time不会是最慢查询的时间,但是会比所有三个查询的总和快得多.

由于我的代码很复杂,我创建了一个简单的用例,我认为,反映了我想要做的很好.我有两种方法:

public static async Task<int> GetAccounts()
{
    int total1 = 0;
    using (SqlConnection connection = new SqlConnection(connString))
    {
        string query1 = "SELECT COUNT(*) FROM [MyDb].[dbo].[Accounts]";
        SqlCommand command = new SqlCommand(query1, connection);
        connection.Open();
        for (int i = 0; i < 19000000; i++)
        {
            string s = i.ToString();
        }
        total1 = (int) await command.ExecuteScalarAsync();
        Console.WriteLine(total1.ToString());
    }
    return total1;
}
Run Code Online (Sandbox Code Playgroud)

和第二种方法:

public static async Task<int> GetDeposits()
{
    int total2 = 0;
    using (SqlConnection connection = new SqlConnection(connString))
    {
        string query2 = "SELECT COUNT(*) FROM [MyDb].[dbo].[Deposits]";
        SqlCommand command = new SqlCommand(query2, connection);
        connection.Open();
        total2 = (int) await command.ExecuteScalarAsync();
        Console.WriteLine(total2.ToString());
    }
    return total2;
}
Run Code Online (Sandbox Code Playgroud)

我称之为:

static void Main(string[] args)
{
    Console.WriteLine(GetAccounts().Result.ToString());

    Console.WriteLine(GetDeposits().Result.ToString());
}
Run Code Online (Sandbox Code Playgroud)

正如你所看到的那样,我GetAccounts()先调用,然后我故意减慢执行速度,所以我有机会执行继续下一个方法.但是我在一段时间内没有得到任何结果,然后我同时在控制台上打印.

所以问题 - 如何使我不等第一种方法完成,以便转到下一个方法.一般来说代码结构并不重要,我真正想要弄清楚的是,是否有任何方法可以使两个查询同时执行.这里的样本是我研究的结果,可能会扩展到我将获得所需结果的程度.

PS我正在使用ExecuteScalarAsync();只是因为我开始使用它的方法.实际上我会使用ScalarReader.

i3a*_*non 16

当您Result在尚未完成的任务上使用该属性时,调用线程将阻塞,直到操作完成.这意味着在您的情况下,GetAccounts操作需要在调用GetDeposits开始之前完成.

如果要确保这些方法是并行的(包括同步CPU密集型部分),则需要将该工作卸载到另一个线程.最简单的方法是使用Task.Run:

static void Main(string[] args)
{
    var accountTask = Task.Run(async () => Console.WriteLine(await GetAccounts()));
    var depositsTask = Task.Run(async () => Console.WriteLine(await GetDeposits()));

    Task.WhenAll(accountTask, depositsTask).Wait();
}
Run Code Online (Sandbox Code Playgroud)

因为Main不能async这样不能使用await你可以简单地调用这个方法并同步等待它完成使用Wait.


aba*_*hev 6

这是一种异步和并行执行两个任务的方法:

Task<int> accountTask = GetAccounts();
Task<int> depositsTask = GetDeposits();

int[] results = await Task.WhenAll(accountTask, depositsTask);

int accounts = results[0];
int deposits = results[1];
Run Code Online (Sandbox Code Playgroud)