2010-07-29 33 views
5

Tengo muchos procesos .NET leyendo mensajes de una tabla SQL Server 2008 DB y procesándolos de uno en uno. Implemento un SP simple para 'bloquear' la fila que está siendo leída por cualquier proceso, para evitar que dos procesos procesen la misma fila.Bloquear una fila de lecturas mientras se ejecuta 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 

Sin embargo, esto tiene defectos: a veces una fila se 'bloquea' y procesa dos veces. Sospecho que hay un problema de concurrencia: dos procesos que leen el estado antes de que uno lo actualice.

Creo que esto podría resolverse implementando un bloque de lectura de alguna manera (es decir, hacer que la transacción bloquee READ), pero no estoy seguro de cómo hacerlo. ¿Alguien puede ayudar?

Gracias mucho por adelantado

Ryan

+0

Debo agregar que los procesos son procesos .NET separados, no una ejecución única de subprocesos múltiples, por lo que esto no se puede resolver a un nivel de programación C#. – Ryan

Respuesta

2

Por favor, intente utilizar:

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

Consulte this enlace para más detalles.

Lo que se está perdiendo es que una declaración SELECT normalmente no bloquear la fila, por lo que si un solo proceso ha ejecutado el SELECT pero no se ejecutará la UPDATE todavía, pero entonces otro proceso llega y ejecuta el SELECT , luego devolverá la fila ya que no la ha bloqueado.

Al usar el UPDLOCK, bloquea esa fila con su instrucción SELECT y evita que el otro proceso la recupere hasta que el primer proceso confirma la transacción, que es el comportamiento que desea.

EDITAR Por supuesto, hacerlo con una sola declaración como sugiere Martin es la forma más fácil y se evita tener que lidiar con el problema de bloqueo en absoluto.

+0

+1 y de hecho, con UPDLOCK podría ser posible idear una solución para bloquear el registro real que está procesando sin necesidad de su estrategia de bloqueo manual;) – StuartLC

3

Por qué no basta con sustituir todo el asunto con

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

Esto evitará 2 tabla de accesos y la celebración de las cerraduras abiertas por más tiempo de lo necesario.

+1

Me gusta su respuesta mejor, ¡muy agradable! – dcp

+0

Doh! Por supuesto. Tu respuesta es muy elegante, gracias. Sin embargo, marqué la respuesta de dcp porque abordaba la pregunta específica que hice sobre el bloqueo de filas. – Ryan

Cuestiones relacionadas