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

标签 c# multithreading ado.net 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);

所以问题开始于此,我需要同时获取所有这三个列表,因为我要将它们传递给显示所有用户数据的 View 。但是,现在执行是同步的(如果我在此处正确使用了该术语),因此收集所有三个产品的数据的总时间为:
Total_Time = Time_To_Get_Accounts + Time_To_Get_Deposits + Time_To_Get_Consignations

这不是很好,因为每个查询都相对较慢,因此总时间相当长,而且accounts查询所花的时间比其他两个查询要多得多,所以今天进入我脑海的想法是:“如果我能同时执行此查询”。也许这是我对该主题最大的误解,但对我而言,最接近这个主意的是使它们异步,因此Total_Time可能不是最慢查询的时间,但比这三个查询的总和要快得多。

由于我的代码很复杂,所以我创建了一个简单的用例,我认为这反射(reflect)了我要做的很好。我有两种方法:
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;
}

第二种方法:
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;
}

我这样称呼它:
static void Main(string[] args)
{
    Console.WriteLine(GetAccounts().Result.ToString());

    Console.WriteLine(GetDeposits().Result.ToString());
}

如您所见,我先调用GetAccounts(),然后故意降低执行速度,因此有机会继续执行下一个方法。但是我在一段时间内没有得到任何结果,然后同时在控制台上打印了所有内容。

因此,问题是-如何进行制作,以便我不等待第一种方法完成,才能转到下一种方法。通常,代码结构不是那么重要,我真正想弄清楚的是,是否有任何方法可以使两个查询同时执行。这里的样本是我研究的结果,可能会扩展到我将获得期望结果的程度。

聚苯乙烯
我之所以使用ExecuteScalarAsync();是因为我从使用它的方法开始。实际上,我将使用ScalarReader

最佳答案

在尚未完成的任务上使用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();
}

因为Main不能是async,所以不能使用await,所以您可以简单地调用此方法并使用Wait同步等待其完成。

关于c# - 如何使两个SQL查询真正异步,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26303954/

相关文章:

c# - 如何使用vs包在解决方案资源管理器中获取所选项目的详细信息

c# - Multi-Tenancy 应用程序 - 自动创建数据库

c# - RAII 是如何在 Java/Scala 等具有垃圾回收功能的语言中替换/实现的?

iphone - NSOperation和NSURLConnection冲突

c# - ExecuteNonQuery 需要一个打开且可用的连接。连接的当前状态是关闭的

c# - session 选项.SecureSocketLayer

在没有连接的情况下调用 C++ Boost 异步服务器处理程序

linux top -Hp [PID] 显示 Node 进程有4个 Node 线程和4个v8工作线程

c# - 如何在 DataReader 上实现 Peek() 函数?

c# - EF 不更新最近添加到数据库的对象的主键