我正在处理一个项目,该项目涉及处理大量文本文件,并导致将记录插入mssql数据库或更新现有信息。
编写sql语句并将其存储在列表中,直到文件处理完毕。 然后处理该列表。每个语句一次只处理一个,但因为这可能是成千上万的语句,并且可能会创建一个非常长的运行过程。
为了尝试加快这个过程,我引入了一些并行处理,但这偶尔会导致以下错误:
事务(进程ID 94)在锁定时死锁通讯 用另一个进程缓冲资源并被选为 僵局受害者。重新运行该交易。
代码如下:
public static void ParallelNonScalarExecution(List<string> Statements, string conn)
{
ParallelOptions po = new ParallelOptions();
po.MaxDegreeOfParallelism = 8;
CancellationTokenSource cancelToken = new CancellationTokenSource();
po.CancellationToken = cancelToken.Token;
Parallel.ForEach(Statements, po, Statement =>
{
using (SqlConnection mySqlConnection = new SqlConnection(conn))
{
mySqlConnection.Open();
using (SqlCommand mySqlCommand = new SqlCommand(Statement, mySqlConnection))
{
mySqlCommand.CommandTimeout = Timeout;
mySqlCommand.ExecuteScalar();
}
}
});
}
我相信更新声明在他们想要实现的目标方面很简单:
UPDATE TableA SET Notes = 'blahblahblah' WHERE Code = 1
UPDATE TableA SET Notes = 'blahblahblah', Date = '2016-01-01' WHERE Code = 2
UPDATE TableA SET Notes = 'blahblahblah' WHERE Code = 3
UPDATE TableA SET Notes = 'blahblahblah' WHERE Code = 4
UPDATE TableB SET Type = 1 WHERE Code = 100
UPDATE TableA SET Notes = 'blahblahblah', Date = '2016-01-01' WHERE Code = 5
UPDATE TableB SET Type = 1 WHERE Code = 101
解决此问题的最佳方法是什么?
答案 0 :(得分:0)
线程A更新资源X并且不提交并且可以继续执行更多更新。 线程B更新资源y并且不提交并且可以继续进行更多更新。 此时,两者都有未提交的更新。
现在,线程A更新资源y并等待线程B的锁定。 线程B没有被任何东西阻挡,所以它继续,最终尝试更新资源x并被锁定A在x上阻塞。 现在他们陷入僵局。这是一个僵局,没有人可以继续提交,所以系统杀了一个。 您必须更频繁地提交以减少死锁的可能性(但这并不能完全消除这种可能性),或者您必须仔细订购更新,以便在继续对y进行任何更新之前完成并完成对x的所有更新
答案 1 :(得分:0)
从我看到你不想做你正在做的事情。我不建议使用多个更新语句来影响不同线程上的相同数据/表。这是种族条件/死锁的滋生。在你的情况下,它应该是安全的,但如果在任何时候你改变了where条件并且存在重叠,你就会遇到竞争条件问题。
如果你真的想用多线程来加速这一点,那么在一个线程上拥有tableA的所有更新语句,在一个线程上拥有tableB上的所有update语句。另一个想法是阻止你的更新声明。
UPDATE TableA SET Notes = 'blahblahblah' WHERE Code IN (1,2,3,4,5)
UPDATE TableA SET Date = '2016-01-01' WHERE Code IN (2,5)
UPDATE TableB SET Type = 1 WHERE Code IN (100,101)
以上这些陈述应该能够在一个简洁的环境中独立执行,因为没有两个陈述会影响同一列吗?