为什么这个锁在服务中不起作用,从asp.net调用?

时间:2015-08-26 11:11:34

标签: c# asp.net multithreading locking structuremap

我继承了一个应用程序,它有一个服务类,可以从asp.net应用程序调用,如下所示:

class PeopleService : IPeopleService
{
    //...
    public void SavePerson(Person person)
    {
        person.UniqueId = _idGenerationService.GetNextId(person);
        //...
        _dbContext.InsertOrUpdate(person);
        _dbContext.Commit();
    }
}

class IdGenerationService : IIdGenerationService
{
    //...
    public string GetNextId(Person person) 
    {
        int nextId = _dbContext.People.Count(x => x.Gender == person.Gender) + 1;
        return string.Format("AB{0}", nextId);
    }
}

GetNextId(Person)的实现不是线程安全的,我们有很多具有重复ID的Person对象,如AB1,AB2,AB2等......

解决方案是将lock应用于SavePerson(Person),如下所示:

class PeopleService : IPeopleService
{
    private static readonly Object ReadIdLock = new Object();
    //...
    public void SavePerson(Person person)
    {
        lock(ReadIdLock) 
        {
            person.UniqueId = _idGenerationService.GetNextId(person);
            //...
            _dbContext.InsertOrUpdate(person);
            _dbContext.Commit();
        }
    }
}

现在我们尝试通过在asp.net应用程序中同时点击“保存”按钮来测试代码。但修复不起作用!有趣的是,只有第一个记录似乎是重复的,如AB1,AB1,AB2,AB3 ......

多个请求如何访问锁定的语句?我应该使用Mutex吗?

(请注意这个例子不是生产代码,它是一个简化的代码来传达我们所遇到的问题。另外,小点,我们使用StructureMap作为DI)。

1 个答案:

答案 0 :(得分:0)

由于lock是一个静态变量,每个appdomain只有一个这样的锁对象,它排除了很多可能的bug。这是我能想到的这个问题的唯一原因:

您的锁只能在一个应用程序域中运行。 ASP.NET可以同时在多个应用程序域中运行应用程序,例如在回收,部署或在Web园式模式下。此外,您可能有多台服务器。

最好的解决方法是使_idGenerationService.GetNextId线程安全。可能需要对数据库查询应用适当的锁定作为此方法的基础。

另外,你锁定区域太长。您还可以将插入内容覆盖到数据库中。这会使并发性匮乏并导致分布式死锁。