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

本文关键字:工作 asp 调用 net 服务 不能 为什么 | 更新日期: 2023-09-27 18:10:04

我继承了一个应用程序,它有一个从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)。

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

由于锁是静态变量,因此每个应用域只有一个这样的锁对象,从而排除了许多可能的错误。对于这个问题,我能想到的唯一原因是:

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

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

同时,你的锁定区域太长。您还介绍了插入数据库的操作。这会导致并发性不足,并可能导致分布式死锁。