2010-07-29 91 views
5

我有许多.NET进程从SQL Server 2008数据库表读取消息,并一次处理一个消息。我实现了一个简单的SP来'锁定'正在被任何一个进程读取的行,以避免任何两个进程处理同一行。从sp中执行读操作时阻止一行sp执行

BEGIN TRAN 

SELECT @status = status FROM t WHERE t.id = @id 
IF @status = 'L' 
BEGIN 
    -- Already locked by another process. Skip this 
    RETURN 0 
END 
ELSE 
    UPDATE t SET status = 'L' WHERE id = @id 
    RETURN 1 
END 

COMMIT 

但是,这是有缺陷的:有时一行被'锁定'并处理两次。我怀疑有一个并发问题:两个进程在更新之前先读取状态。

我认为这可以通过以某种方式实现读取块(即使事务块读取)来解决,但我不确定如何执行此操作。谁能帮忙?

感谢很多提前

瑞安

+0

我应该补充一点,进程是独立的.net进程,而不是多线程的单个执行,所以这不能在c#编程级别解决。 – Ryan 2010-07-29 13:11:15

回答

2

你有没有尝试使用:

SELECT @status = status FROM t (UPDLOCK) WHERE t.id = @id 

参考this联系了解更多详情。

你缺少的东西是一个SELECT声明通常不会锁定该行,所以如果一个进程已经执行SELECT但一直没有执行UPDATE还,但随后另一个进程出现了,执行SELECT ,那么它会将行返回,因为您尚未锁定它。

通过使用UPDLOCK,您可以使用SELECT语句锁定该行,并防止其他进程返回,直到第一个进程提交事务,这是您想要的行为。

编辑 当然与马丁提出一个语句这样做是最简单的方式,您就不必在所有处理锁定问题。

+0

+1,实际上,使用UPDLOCK可能会设计一个解决方案来锁定您正在处理的实际记录,而不需要您手动锁定策略;) – StuartLC 2010-07-29 13:25:00

3

为什么不更换整个事情与

UPDATE t SET status = 'L' WHERE id = @id and status <> 'L' 
RETURN @@ROWCOUNT 

这将避免2台访问,并持有锁打开任何超过必要的时间。

+1

我更喜欢你的答案,非常好! – dcp 2010-07-29 13:25:19

+0

Doh!当然。你的回答非常优雅,谢谢。但是,我标记了dcp的答案,因为它解决了我询问的关于行锁定的具体问题。 – Ryan 2010-07-29 13:35:40