来自后台工作者冲突的查询?
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。这里没有经典的 线程安全 问题,但是数据库不能免受数据竞争的影响(这是一个完全不同的主题)......说到这一点,您在此方法中所做的任何其他事情都可能不是,.. 例如,getSerialNumber
、getTestResult
如果有是问题。您使用 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
,有BeginExecute
和EndExecute
方法可以适应Task.FromAsync 的任务。即使没有Microsoft.Bcl.Async
,您也可以使用任务。您必须使用ContinueWith
来处理延续并执行下一个命令,或者返回 UI 线程
interesant ..我会检查一下...回到问题,如果每个新的 bkgroundworker 都进行新的连接/查询,对吗?谢谢!【参考方案2】:
在这里读取连接字符串不是问题。如果您要通过多个线程共享 SqlConnection 对象,您将遇到问题。但您的代码中并非如此。
【讨论】:
是的,你也这么想。这是我工作中的一个新系统,我不想破坏一些东西哈哈哈谢谢!【参考方案3】:我相信这是来自ACID 属性的关于Isolation
的问题。请看看他们。
根据 SQL 标准,单个 SQL 查询在查询所处理的表的稳定(一致)状态上运行。所以这个定义表明,它在执行时看不到任何变化。然而,据我所知,并不是所有的 DBMS 软件都完全遵循这个规则。例如,有些产品和/或隔离级别允许脏读。
这是来自另一个用户的非常详细的explanation。
【讨论】:
以上是关于来自后台工作者冲突的查询?的主要内容,如果未能解决你的问题,请参考以下文章