首页 > 解决方案 > 异步更新多行立即更新1/4的行然后等待

问题描述

我有一个代码可以异步更新 SQL Server 表中的多行。我在更新 540 行时对其进行了测试,并在表中即时更新了 144 行,然后等待大约 5 分钟,然后更新其余的行。至少这是当我使用 SELECT 检查更新的行时的样子。我想知道为什么会这样。

整个事情是由按钮的点击触发的:

DialogResult res = MessageBox.Show($"Znaleziono {num} pasujących maszyn. Czy chcesz zaktualizować priorytet maszyny danymi z pliku?", "Potwierdź", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
            if(res == DialogResult.Yes)
            {
                await UpdatePriority();
                MessageBox.Show("Updated!");

这是 UpdatePriority 方法,它为项目列表中的所有位置异步调用 place.Edit() 方法:

public async Task<string> UpdatePriority()
        {
            List<Task<string>> UpdateTasks = new List<Task<string>>();

            try
            {
                foreach (Place p in Items.Where(i => i.IsUpdated==true))
                {
                    UpdateTasks.Add(Task.Run(()=> p.Edit()));
                }

                string response = "OK";

                IEnumerable<string> res = await Task.WhenAll<string>(UpdateTasks);

            }
            catch (Exception ex)
            {
                throw;
            }
            return "Nie udało się zaktualizować danych żadnego zasobu..";
        }

这是地方对象的 Edit() 方法。它基本上更新了 SQL Server 表中的位置数据:

public async Task<string> Edit()
        {
            string iSql = @"UPDATE JDE_Places
                            SET Priority=@Priority
                            WHERE PlaceId=@PlaceId";
            string msg = "OK";


            using (SqlCommand command = new SqlCommand(iSql, Settings.conn))
            {
                command.Parameters.AddWithValue("@PlaceId", PlaceId);
                command.Parameters.AddWithValue("@Priority", Priority);

                int result = -1;

                try
                {
                    result = await command.ExecuteNonQueryAsync();
                    IsUpdated = false;
                }
                catch (Exception ex)
                {
                    msg = $"Wystąpił błąd przy edycji zasobu {Name}. Opis błędu: {ex.Message}";
                }

            }
            return msg;
        }

这是用作可重用连接对象的 Settings conn 属性:

public static class Settings
    {
        private static SqlConnection _conn { get; set; }
        public static SqlConnection conn
        {
            get
            {
                if (_conn == null)
                {
                    _conn = new SqlConnection(Static.Secrets.ConnectionString);
                }
                if (_conn.State == System.Data.ConnectionState.Closed || _conn.State == System.Data.ConnectionState.Closed)
                {
                    try
                    {
                        _conn.Open();
                    }
                    catch (Exception ex)
                    {
                        MessageBox.Show("Nie udało się nawiązać połączenia z bazą danych.. " + ex.Message);
                    }

                }
                return _conn;
            }
        }
    }

我意识到将连接保持在 using 语句中可能会更好(而不是重用它),但是当我将它添加到 place.Edit() 方法时,它的工作速度甚至更慢(并且不可靠)。

更新:我又跑了几个测试,他们添加 540 行所花费的时间从 15 秒到 400 秒不等。然后我只是在地方对象的 Edit() 中更改result = await command.ExecuteNonQueryAsync()result = command.ExecuteNonQuery()再跑了几个测试,全部在 10 秒内完成!不过,我不知道为什么 ExecuteNonQuery() 的异步版本比非异步版本差这么多。单个 Edit() 方法使用 ExecuteNonQuery() 大约需要 0.1 秒,使用 ExecuteNonQueryAsync() 大约需要 1 - 400 秒。以下是日志:ExecuteNonQuery() ExecuteNonQueryAsync()

标签: c#sql-serverasynchronous

解决方案


你的问题是你的Settings班级。您实际上是在尝试SqlConnection在多个Sqlcommands 中使用相同的对象。SqlConnection像这样使用时不是线程安全的。你最终会得到多个命令,因为你的代码是非阻塞和异步的。这就是导致您的代码“等待”(或死锁)的原因。这就是为什么当您运行它时同步(没有ExecuteNonQueryAsync等)它可以正常工作。

反正你根本不需要这个对象。ADO.Net 为您处理连接池,因此重用它没有任何优势SqlConnection。只需为每个创建一个新的SqlCommand

public async Task<string> Edit()
{


       using (SqlConnection conn = new SqlConnection(...))
       using (SqlCommand command = new SqlCommand(iSql, conn))
       {
       ...
       }
   }

你应该会发现你的“等待”消失了。


推荐阅读