c#用于批量复制的锁表

时间:2014-01-04 16:08:09

标签: c# sql sql-server locking

我在c#中有一个数据表,每隔几毫秒就会获得一个新行。我希望这个数据表每隔10秒在SQL服务器中进行批量复制,但是我得到一个例外“收集被修改;枚举操作可能无法执行”所以我使用锁来绕过该错误。锁是一种好方法还是有更好的选择呢?

lock (testdatatable.Rows.SyncRoot)
{
     s.WriteToServer(testdatatable);
}

3 个答案:

答案 0 :(得分:0)

我不是C#guru,但是喜欢玩任何与SQL服务器相关的东西。

对我来说,您将ADO数据表锁定在内存中。由于您没有发布其余的代码,我无法判断它是否与SQL Server表相关联。我打赌你是。

该消息表明未说明的数据已被修改。

是否有一个批量复制流程踩到另一个?

查看ConcernedOfTunbridgeWells的条目以获得良好的编码风格 -

Best Practices for uploading files to database

我的主要问题是你为什么每10秒做一次?

在那个频率上,有些事情浮现在脑海中。

1 - 您使用的是没有索引和完整性的临时表吗?如果数据大小很大,则重建索引需要时间。

2 - 底层的SqlBulkCopy类用于锁定是什么。 TABLOCK可能会以此速率导致阻塞。

如果你有sysadmin访问数据库的权限,这里有一些简单的命令来查看锁。

--
-- Locked object details
-- 

-- Old school technique
EXEC sp_lock
GO

-- Lock details
SELECT
    resource_type, resource_associated_entity_id,
    request_status, request_mode,request_session_id,
    resource_description 
    FROM sys.dm_tran_locks
    WHERE resource_database_id = DB_ID('AdventureWorks2012')
GO

-- Page/Key details
SELECT object_name(object_id) as object_nm, *
    FROM sys.partitions
    WHERE hobt_id = 72057594047037440
GO

-- Object details
SELECT object_name(1266103551)
GO

批量复制的技术下一页代码。

http://technet.microsoft.com/en-us/library/ms130809.aspx

TABLOCK:在批量复制操作期间获取表级锁。此选项可显着提高性能,因为仅在批量复制操作期间保持锁定可减少表上的锁争用。如果表没有索引并且指定了TABLOCK,则可以同时由多个客户端加载表。默认情况下,锁定行为由批量加载时的表选项表锁定确定。

总之,尝试增加BULK COPY操作之间的时间。查看错误是否消失。

答案 1 :(得分:0)

也许这个问题应该用“c#”标记而不是sql?

您的问题(我认为)与您修改集合有关,对吧? 这是一个普遍的问题,我认为你可以在这里找到解决方案:

"Collection was modified..." Issue

如果没有,那么我们必须查看您的代码以进行更多调查。

答案 2 :(得分:0)

这个问题与:

无关
  • SQL Server
  • 锁定(好吧,排序,请参阅答案的底部)

这里的根本问题是批量复制将枚举集合。当这种情况发生时,你正在改变这个系列。

就是这样。

.NET中的几乎所有枚举器实现都将与它所枚举的集合合作,如果集合在枚举器仍处于活动状态时发生更改,则枚举器将停止工作。

示例:

  • 我说,对于此页面上的每个人,我想拨打列出的电话号码并与该人交谈
  • 当我这样做时,你会在网页上添加一个新人

问题:

  • 我应该打电话给那个人,因为他现在在页面上,当我拨打最后一个(就像我刚开始时),并完成那个电话,下一个人应该是你添加的那个人,所以我应该打电话给那个人?
  • 当我开始时,他/她不在页面上而忽视那个人?

如果您在拨打电话或与该人通话时从列表中删除某人,该怎么办?我应该挂断电话吗?

如果底层集合在枚举时发生更改,则大多数.NET集合枚举器将停止工作,具有该精确异常。

“修复”这个问题的常用方法是复制该集合。

基本上,不要这样做:

foreach (var x in y)

你这样做:

foreach (var x in y.ToArray())

.ToArray()调用将复制到一个单独的数组中,然后整个for循环将对其进行枚举。如果y平均时间发生变化,则无关紧要。

但是,在你的情况下,你离实际的for循环或类似的东西还不远。

我会确保此处的DataTable在您将其批量复制到数据库时不会发生变化。

锁是不正确的,至少不是自己做的,因为任何其他不锁定的代码当然会完全忽略锁。 lock语句不会神奇地阻止其他线程更改实例,它只是对实例锁定其他尝试锁定的线程也会停止这样做。如果在修改对象之前没有其他线程尝试锁定该对象,则锁定基本上什么也不做。