简体   繁体   English

异步调用按顺序执行

[英]Async calls are executing sequentially

I have an async database call inside a loop.我在循环中有一个异步数据库调用。 But the database calls are happening in a sync way.但是数据库调用是以同步方式发生的。 I am not sure the calls are not happening in an async fashion.我不确定这些调用是否以异步方式发生。 Any directions on what is wrong with the below code.有关以下代码有什么问题的任何说明。

static async Task Main(string[] args)
{
      long executionTime = 0;
      await SequentialDBCallAsync();
}

private static async Task SequentialDBCallAsync()
{
    DBLayer dB = new DBLayer();

    for (int i = 0; i < 10; i++)
    {
        var product = await dB.GetDataByNameAsync("Name - 100000");
        Console.WriteLine(i);
    }
}

public async Task<Product> GetDataByNameAsync(string name)
{
    using (var sql = new SqlConnection("Data Source=(localdb)\\ProjectsV13;Initial Catalog=Inventory;Integrated Security=True;Connect Timeout=30;Encrypt=False;TrustServerCertificate=False;ApplicationIntent=ReadWrite;MultiSubnetFailover=False"))
    {
        sql.Open();
        string query = "Select id, Name from Product where Name = @name";

        SqlCommand cmd = new SqlCommand(query, sql);
        cmd.Parameters.Add("@name", System.Data.SqlDbType.VarChar).Value = name;

        Console.WriteLine("started");
        var reader = await cmd.ExecuteReaderAsync();
        Console.WriteLine("executing");

        while (await reader.ReadAsync())
        {
            var product = new Product()
            {
                Id = reader.GetInt32(0),
                Name = reader.GetString(1)
            };

            return product;
        }

        return new Product();
    }
}

Async calls are executing sequentially... But the database calls are happening in a sync way.异步调用是按顺序执行的……但是数据库调用是以同步的方式发生的。

Your first statement was correct;您的第一个陈述是正确的; the second one was incorrect.第二个是不正确的。

await is often used to make asynchronous calls execute sequentially (one at a time). await通常用于使异步调用按顺序执行(一次一个)。 They are still asynchronous, though;但是,它们仍然是异步的; they're not synchronous.它们不是同步的。 Indeed, the whole point of async / await is for control flow to remain natural while being asynchronous instead of synchronous.事实上, async / await的全部意义在于控制流在异步而不是同步的同时保持自然。

For most code, you can use asynchronous concurrency (eg, Task.WhenAll ).对于大多数代码,您可以使用异步并发(例如Task.WhenAll )。 This doesn't work as well for databases, though, because only one query can be active per db connection .但是,这对于数据库来说效果不佳,因为每个 db connection 只能激活一个查询 This is a limitation of the on-the-wire database protocols (although nothing prevents a future database from removing that limitation).这是在线数据库协议的一个限制(尽管没有什么能阻止未来的数据库消除该限制)。 So, in order to use a Task.WhenAll -based solution, you'd have to create that many database connections .因此,为了使用基于Task.WhenAll的解决方案,您必须创建那么多数据库连接 While occasionally useful, this is not always a good idea.虽然偶尔有用,但这并不总是一个好主意。

Instead, what you want to do is restructure your query itself so that it does all the work in a single query.相反,您要做的是重组查询本身,以便它在单个查询中完成所有工作。 Instead of running a N queries that each retrieve a single row, run a single query that returns N rows.运行一个返回 N 行的单个查询,而不是运行每个检索单行的 N 个查询。

Your current code awaits for each invocation to finish, ie, it does not proceed to the next for cycle until dB.GetDataByNameAsync does not finish.您当前的代码等待每次调用完成,即它不会继续进行下一个for循环,直到dB.GetDataByNameAsync未完成。

If you want them to execute at the same time you should have something like this:如果你想让它们同时执行,你应该有这样的东西:

var tasks = Enumerable.Range(0, 10).Select(i => dB.GetDataByNameAsync("Name - 100000")).ToList();
await Task.WhenAll(tasks);

It will create all the tasks and only then it will wait for all of them to finish.它将创建所有任务,然后才会等待所有任务完成。

However, you can also process them while they are returning, like so:但是,您也可以在它们返回时对其进行处理,如下所示:

while (tasks.Any())
{
    var finishedTask = await Task.WhenAny(tasks);
    tasks.Remove(finishedTask);
    
    // process task
}

While Task.WhenAll (or even better Parallel.ForEachAsync (twitter link)) will execute the tasks concurrently, getting records one by one is very inefficient.虽然Task.WhenAll (甚至更好的Parallel.ForEachAsync (twitter 链接))将同时执行任务,但一条一条地获取记录是非常低效的。

The signature of the method should be something like this:该方法的签名应该是这样的:

public async Task<List<Product>> GetDataByNameAsync(IEnumerable<string> names)

and it should perform a single query: SELECT id, Name FROM Product WHERE Name IN (...,...,...) .它应该执行一个查询: SELECT id, Name FROM Product WHERE Name IN (...,...,...)

In order to use WHERE Name IN in the code you can:为了在代码中使用WHERE Name IN ,您可以:

  1. See Parameterize an SQL IN clause请参阅参数化 SQL IN 子句
  2. Use Dapper, please see List support (I shortened the original example queries):使用 Dapper,请参阅列表支持(我缩短了原始示例查询):

    For example:例如:

    connection.Query<int>("select * from... where Id in @Ids", new { Ids = new int[] { 1, 2, 3 } });

    Will be translated to:将被翻译成:

    select * from... where Id in (@Ids1, @Ids2, @Ids3)"

  3. Use Entity Framework and then LINQ Where in collection clause .使用实体框架,然后使用LINQ Where in collection 子句

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM