0

我们有一个长期运行的用户操作,由工作进程池处理。数据输入和输出来自 Azure SQL。

主 Azure SQL 表结构列近似为

[UserId, col1, col2, ... , col N, beingProcessed, lastTimeProcessed ] 

beingProcessed是布尔值并且lastTimeProcessed是日期时间。每个工人角色的逻辑如下所示,并且有多个工人处理(每个工人都有自己的实体框架层),本质上beingProcessed是用于互斥目的的锁

问题:如何beingProcessed根据上述负载处理“锁”本身的并发问题?我认为 read-modify-writebeingProcessed需求的操作必须是原子的,但我对其他策略持开放态度。也对其他代码改进开放。

[更新]:我想知道TransactionScope这里是否需要... http://msdn.microsoft.com/en-US/library/system.transactions.transactionscope(v=vs.110).aspx

代码:

public void WorkerRoleMain()
{
    while(true)
    {
        try
        {
            dbContext db = new dbContext();

            // Read
            foreach (UserProfile user in db.UserProfile
                    .Where(u => DateTime.UtcNow.Subtract(u.lastTimeProcessed) 
                            > TimeSpan.FromHours(24) & 
                            u.beingProcessed == false))
            {
                user.beingProcessed = true; // Modify
                db.SaveChanges();           // Write
                // Do some long drawn processing here
                ...
                ...
                ...
                user.lastTimeProcessed = DateTime.UtcNow;
                user.beingProcessed = false;
                db.SaveChanges();
            }
        }
        catch(Exception ex)
        {
            LogException(ex);
            Sleep(TimeSpan.FromMinutes(5));
        }
    } // while ()
}
4

1 回答 1

0

我们通常做的是这样的:

在长操作开始时,我们开始一个事务:

BEGIN TRANSACTION

然后我们使用这些提示从表中选择我们要更新/删除的行:

SELECT * FROM Table WITH (ROWLOCK, NOWAIT) Where ID = 123;

然后我们检查我们是否有该行。如果该行被另一个进程锁定,则会出现 SQL 错误。在这种情况下,我们回滚事务并通知用户。如果记录被锁定,我们处理记录,并使用我们用来锁定记录的相同事务对象进行所需的更新:

UPDATE Table SET Col1='value' WHERE ID = 123;

然后我们提交事务。

COMMIT;

这只是该过程的伪代码。你必须在你的程序中实现它。

关于上述过程的一点小提示。在 SQL Server(或 Azure)中锁定记录时,请在 WHERE 子句中使用主键,否则 SQL Server 将决定使用页锁或表锁

于 2012-12-13T00:19:26.227 回答