簡體   English   中英

異步調用按順序執行

[英]Async calls are executing sequentially

我在循環中有一個異步數據庫調用。 但是數據庫調用是以同步方式發生的。 我不確定這些調用是否以異步方式發生。 有關以下代碼有什么問題的任何說明。

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();
    }
}

異步調用是按順序執行的……但是數據庫調用是以同步的方式發生的。

您的第一個陳述是正確的; 第二個是不正確的。

await通常用於使異步調用按順序執行(一次一個)。 但是,它們仍然是異步的; 它們不是同步的。 事實上, async / await的全部意義在於控制流在異步而不是同步的同時保持自然。

對於大多數代碼,您可以使用異步並發(例如Task.WhenAll )。 但是,這對於數據庫來說效果不佳,因為每個 db connection 只能激活一個查詢 這是在線數據庫協議的一個限制(盡管沒有什么能阻止未來的數據庫消除該限制)。 因此,為了使用基於Task.WhenAll的解決方案,您必須創建那么多數據庫連接 雖然偶爾有用,但這並不總是一個好主意。

相反,您要做的是重組查詢本身,以便它在單個查詢中完成所有工作。 運行一個返回 N 行的單個查詢,而不是運行每個檢索單行的 N 個查詢。

您當前的代碼等待每次調用完成,即它不會繼續進行下一個for循環,直到dB.GetDataByNameAsync未完成。

如果你想讓它們同時執行,你應該有這樣的東西:

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

它將創建所有任務,然后才會等待所有任務完成。

但是,您也可以在它們返回時對其進行處理,如下所示:

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

雖然Task.WhenAll (甚至更好的Parallel.ForEachAsync (twitter 鏈接))將同時執行任務,但一條一條地獲取記錄是非常低效的。

該方法的簽名應該是這樣的:

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

它應該執行一個查詢: SELECT id, Name FROM Product WHERE Name IN (...,...,...)

為了在代碼中使用WHERE Name IN ,您可以:

  1. 請參閱參數化 SQL IN 子句
  2. 使用 Dapper,請參閱列表支持(我縮短了原始示例查詢):

    例如:

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

    將被翻譯成:

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

  3. 使用實體框架,然后使用LINQ Where in collection 子句

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM