我的问题是:本周早些时候,我接到了加快我们项目中一项任务的任务。我看了一下,立刻想到了在该任务中为函数使用并行foreach循环的想法。
我实现了它,完成了函数(包括所有子函数),并更改了SqlConnections(和其他东西),使其能够并行运行。我开始了整件事,一切进展得又好又快(仅此一项就将完成任务的时间减少了约45%)
现在,昨天我们想用更多的数据来尝试同样的事情。。。我遇到了一些奇怪的问题:每当调用并行函数时,它就完成了它的工作。。。但有时其中一个线程会挂起至少4分钟(对于连接AND命令,超时设置为1分钟)。
如果在此期间暂停程序,我会看到该循环中只有一个线程仍然处于活动状态,并且它挂起
connection.Open()
大约4分钟后,程序简单地继续运行,而不会引发错误(除了输出框中的一条消息,说某个地方发生了异常,但我的应用程序没有捕捉到它,而是在SqlConnection/SqlCommand对象中的某个地方)。
我可以在不发生任何事情的情况下终止MSSQLServer上的所有连接,而且MSSQLServer在这4分钟内什么也不做,所有连接都是空闲的。
这是用于向数据库发送更新/插入/删除语句的过程:
int i = 80;
bool itDidntWork = true;
Random random = new Random();
while (itDidntWork && i > 0)
{
try
{
using (SqlConnection connection = new SqlConnection(sqlConnectionString))
{
connection.Open();
lock (connection)
{
command.Connection = connection;
command.ExecuteNonQuery();
}
itDidntWork = false;
}
}
catch (Exception ex)
{
if (ex is SqlException && ((SqlException)ex).ErrorCode == -2146232060)
{
Thread.Sleep(random.Next(500, 5000));
}
else
{
SqlConnection.ClearAllPools();
}
Thread.Sleep(random.Next(50, 110));
i--;
if (i == 0)
{
writeError(ex);
}
}
}
以防万一:在较小的数据库上可能会发生死锁(错误编号2146232060),所以如果发生死锁,我必须使冲突语句在不同的时间发生。即使在小型数据库/小型服务器上也能很好地工作。如果错误不是由死锁引起的,那么很可能是连接有故障,所以我正在清理所有断开的连接。
存在用于执行标量、填充数据表/数据集的类似函数(是的,应用程序是
那个
旧的)和执行存储过程。
是的,所有这些都用在了并行循环中。
有人知道那里会发生什么吗?或者我想知道那里发生了什么?
*编辑命令对象:
它被赋予函数,当命令对象被赋予函数时,它总是一个新的对象。
关于锁:如果我把锁收起来,我会收到成百上千的“连接已关闭”或“连接已打开”错误,因为open()函数只是从.NET的连接池中获取一个连接。锁确实按预期工作。
示例代码:
using(SqlCommand deleteCommand = new SqlCommand(sqlStatement))
{
ExecuteNonQuerySafely(deleteCommand); // that's the function that contains the body I posted above
}
*编辑2
我得改正一下:它取决于这个
command.Connection = connection;
至少我想是的,因为当我暂停应用程序时,“step”标记thingi是绿色的
command.ExecuteNonQuery();
说这就是下一步要执行的语句。
*编辑3
只是为了确保我刚刚开始了另一个测试,在连接对象周围没有任何锁。。。需要几分钟才能得到结果。
*编辑4
嗯,我错了。我删除了锁定语句。。。它仍然有效。也许我第一次尝试它时,有一个重复使用的连接或其他什么。谢谢你指出这一点。
*编辑5
我感觉这种情况只发生在对特定数据库过程的一次特定调用中。
我不知道为什么。就C#而言,该调用与其他调用没有区别
参见编辑6
。由于它当时没有执行该语句(我想。也许有人可以纠正我的错误。如果在调试模式下,一行标记为绿色(而不是黄色),它还没有执行该声明,而是等待该行之前的语句完成,这是正确的吗?)这很奇怪。
*编辑6
有3个命令对象在整个时间内都被重用。它们是在平行函数之上定义的。我不知道那有多糟糕。它们只用于调用一个存储过程(每个存储过程称为不同的过程),当然具有不同的参数和新的连接(通过上述方法)。
*编辑7
好吧,实际上只有当调用一个特定的存储过程时才会调用。除了它在挂起的连接对象的赋值上(下一行标记为绿色)。
试图弄清楚这是什么原因造成的。
*编辑8
是的,这只是发生在另一个指挥部。就这样。
*编辑9
好的。问题解决了。“挂起”实际上是CommandTimeouts,设置为10分钟(!)。它们只为两个命令设置(我在编辑7中提到的一个和我在编辑8中提到的那个)。由于我在重组我的命令以使其像devunde建议的那样时发现了这两个问题,我将他的答案标记为解决了我的问题的答案。此外,他关于限制for循环使用的线程数量的建议进一步加快了进程。
特别感谢Marc Gravell解释了这些事情,并在周六和我一起呆在这里;)