来自后台工作者冲突的查询?

Posted

技术标签:

【中文标题】来自后台工作者冲突的查询?【英文标题】:Queries from background worker conflict? 【发布时间】:2021-01-27 09:57:11 【问题描述】:

如果我使用相同的 ConnectionString 从多个线程执行查询,是否会出现问题?如果两个或多个线程同时尝试发送数据会怎样?

string globalConnectionString = @"some_stringHere!";

//create new backgroundWorker if new logFile is created (txt file).
// ....
    
private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)

   // get some data from created logFile
   string serialNumber = getSerialNumber(logFile);
   string testResult = getTestResult(logFile);
       
   // if server is online, send data
   if(serverIsOnline)
   
      using(SqlConnection connection = new SqlConnecton(globalConnectionString))
      
         SqlCommand someCommand = new SqlCommand("some insert/update command here!", connection);
         connection.Open();
         Command.ExecuteNonQuery();
         connection.Close();
      
   

【问题讨论】:

您有实际问题吗? 一开始不要使用 BGW。自 2012 年引入 async/await 以来,它已过时。您可以将 BGW 替换为await command.ExecuteNonQueryAsync() 您的SqlConnection 被包裹在using 语句 中,并在每次调用method 时创建一个新实例,SqlCommand 也是如此。此外,database 旨在处理许多 concurrent 连接,并且所有这些都被设计为必要时的 connection pool。这里没有经典的 线程安全 问题,但是数据库不能免受数据竞争的影响(这是一个完全不同的主题)......说到这一点,您在此方法中所做的任何其他事情都可能不是,.. 例如,getSerialNumbergetTestResult 如果有问题。您使用 BGW - 您的代码变得过于复杂,协调多个异步调用变得困难,而按顺序执行它们变得根本不可能。 使用相同的连接执行异步命令没有问题,只要它们是按顺序执行的,即await cmd1.Execute..Async(); await cmd.Execute...Async();。同时打开多个连接并让每个连接执行命令没有问题,但可能存在性能问题,因为每个连接都获取自己的锁并可能阻塞其他连接 你为什么要问这个问题?数据库在并发连接方面没有问题(SQLite 除外)。另一方面,如果您想通过并行执行多次来加快慢查询速度,您可能会增加而不是减少延迟。所有查询都通过相同的网卡发送,由相同的服务器、相同的 CPU、相同的磁盘等提供服务。许多数据库也并行化查询处理。大多数情况下,改进查询比尝试从客户端并行化查询要好得多(也更容易) 【参考方案1】:

如果使用正确,并发连接是可以的

如果出于正确的原因,同时使用多个连接是没有问题的。数据库可以处理数千个并发客户端连接。

并行执行相同的慢查询以使其更快完成可能会使它更慢,因为每个连接都可能阻塞其他连接。许多数据库已经对查询处理进行了并行处理,产生的结果比粗略的客户端并行处理要好得多。

如果您想让慢速查询运行得更快,则可以通过调查其慢速的原因并修复性能问题来获得更好的结果。例如,如果你想插入 10K 行,使用 SqlBulkCopy 或 BULK INSERT 加载行比执行 10K INSERT 更快,这最终会相互阻塞以访问同一个表甚至数据页

您可以使用相同的连接来执行异步查询(例如ExecuteNonQueryAsync()ExecuteReaderAsync() 等,前提是它们一个接一个地执行。您不能在同一个连接上执行多个并发查询,至少不能没有经历了一些困难。

真正的问题

真正的问题是首先使用 BackgroundWorker。自 2012 年引入 async/await 以来,该类已过时。使用 BGW 组合多个异步操作非常困难。进度报告可通过Progress<T> 类获得,合作取消可通过CancellationTokenSource 获得。详情请查看Async in 4.5: Enabling Progress and Cancellation in Async APIs。

您可以仅将代码中的 BGW 调用替换为 await command.ExecuteNonQueryAsync()。您可以创建一个异步方法来执行将数据插入数据库:

private async Task InsertTestData(string serialNumber,string testResult)

   // if server is online, send data
   if(serverIsOnline)
   
      using(SqlConnection connection = new SqlConnecton(globalConnectionString))
      
         var someCommand = new SqlCommand("some insert/update command here!", connection);
         someCommand.Parameters.Add("@serial",SqlDbType.NVarChar,30).Value=serialNumber;
         ...
         connection.Open();
         Command.ExecuteNonQueryAsync();
      
   

如果获取序列号和测试数据比较耗时,可以使用Task.Run在后台分别运行:

   string serialNumber = await Task.Run(()=>getSerialNumber(logFile));
   string testResult = await Task.Run(()=>getTestResult(logFile));
   await InsertTestData(serialNumber,testResult);

您还可以使用像 Dapper 这样的库来简化数据库:

private async Task InsertTestData(string serialNumber,string testResult)

   // if server is online, send data
   if(serverIsOnline)
   
      using(SqlConnection connection = new SqlConnecton(globalConnectionString))
      
         await connection.ExecuteAsync("INSERT .... VALUES(@serial,@test)",
                               new serial=serialNumber,test=testResults);
      
   

Dapper 将生成参数化查询,并将查询中的参数与匿名对象中的属性按名称进行匹配。

【讨论】:

我不赞成,你为此付出的努力比我多 10 倍,并且应该完全回答问题等等 @MichaelRandall 如果您考虑此类问题出现的频率,只需再次编写相同的答案,修改以匹配问题。另一方面,我们仍然缺少为什么问这个问题。这是一个普遍的问题,还是 OP 试图提高慢批量插入或更新的性能?每种情况都需要不同(但也很常见)的答案。 感谢非常好的答案!!,我正在使用 backgroundWorker,因为工业机器是 WinXP 框架 4.0。机器对 PCB 板进行电气元件测试,当测试完成时,它会创建带有测试结果的 txt 文件(每个 PCB 一个 txt 文件)。为了避免在从每个 txt 文件计算 serialNumber 和 testResult 时 GUI 阻塞,我决定使用多线程.. 如果你添加了Microsoft.Bcl.Async包,你可以使用async/await。没有Execute...Async,有BeginExecuteEndExecute 方法可以适应Task.FromAsync 的任务。即使没有Microsoft.Bcl.Async,您也可以使用任务。您必须使用ContinueWith 来处理延续并执行下一个命令,或者返回 UI 线程 interesant ..我会检查一下...回到问题,如果每个新的 bkgroundworker 都进行新的连接/查询,对吗?谢谢!【参考方案2】:

在这里读取连接字符串不是问题。如果您要通过多个线程共享 SqlConnection 对象,您将遇到问题。但您的代码中并非如此。

【讨论】:

是的,你也这么想。这是我工作中的一个新系统,我不想破坏一些东西哈哈哈谢谢!【参考方案3】:

我相信这是来自ACID 属性的关于Isolation 的问题。请看看他们。

根据 SQL 标准,单个 SQL 查询在查询所处理的表的稳定(一致)状态上运行。所以这个定义表明,它在执行时看不到任何变化。然而,据我所知,并不是所有的 DBMS 软件都完全遵循这个规则。例如,有些产品和/或隔离级别允许脏读。

这是来自另一个用户的非常详细的explanation。

【讨论】:

以上是关于来自后台工作者冲突的查询?的主要内容,如果未能解决你的问题,请参考以下文章

Solr 后台查询实例 (工作备查)

来自不同用户的位于 OneDrive 上的 Excel 工作簿的 Excel 查询

PostgreSQL 错误:由于与恢复冲突而取消语句

tomcat编码问题

C# 后台工作人员 - 我应该同时使用多少?

数据表过滤器在应用SQL Server 2008分页代码时无法正常工作