2010-07-29 2 views
5

De nombreux processus .NET lisent les messages d'une table DB SQL Server 2008 et les traitent un à la fois. J'implémente un simple SP pour «verrouiller» la ligne lue par un processus, pour éviter que deux processus ne traitent la même ligne.Bloquer une ligne à partir des lectures pendant que sp s'exécute

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 

Cependant, ceci est erronée: parfois une rangée se « verrouillé » et traité deux fois. Je soupçonne qu'il y a un problème de concurrence: deux processus lisant l'état avant de le mettre à jour.

Je pense que cela pourrait être résolu en mettant en œuvre un bloc de lecture en quelque sorte (c'est-à-dire que le bloc de transactions READs), mais je ne sais pas comment faire cela. Quelqu'un peut-il aider?

Merci beaucoup à l'avance

Ryan

+0

Je devrais ajouter que les processus sont des processus .net distincts, pas une exécution unique multithread, donc cela ne peut pas être résolu à un niveau de programmation C#. – Ryan

Répondre

2

Avez-vous essayez d'utiliser:

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

Se reporter à this lien pour plus de détails.

La chose vous manque est qu'une déclaration SELECT ne se verrouille pas normalement la ligne, donc si un processus a exécuté l'SELECT mais n'a pas exécuté l'encore UPDATE, mais un autre processus arrive et exécute la commande SELECT , alors ça va retourner la rangée puisque vous ne l'avez pas verrouillé. En utilisant le UPDLOCK, vous verrouillez cette ligne avec votre instruction SELECT et empêchez l'autre processus de la récupérer jusqu'à ce que le premier processus valide la transaction, ce qui correspond au comportement que vous souhaitez.

EDIT Bien sûr, le faire avec une déclaration comme Martin suggère est la meilleure façon et vous éviter d'avoir à traiter la question de verrouillage du tout.

+0

+1 et en fait, avec UPDLOCK il pourrait être possible de concevoir une solution pour verrouiller l'enregistrement réel que vous traitez sans avoir besoin de votre stratégie de verrouillage manuel du tout;) – StuartLC

3

Pourquoi ne pas remplacer juste la chose entière avec

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

Cela évitera 2 tableau accès et la tenue des verrous ouverts plus longtemps que nécessaire.

+1

J'aime mieux votre réponse, très bien! – dcp

+0

Doh! Bien sûr. Votre réponse est très élégante, merci. Cependant, j'ai marqué la réponse de dcp parce qu'elle traitait de la question spécifique que je posais à propos du verrouillage de ligne. – Ryan

Questions connexes