为什么这个锁不能在服务中工作,从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可以同时在多个应用域中运行应用,例如在回收、部署或web花园模式下。此外,您可能有多个服务器。
最好的解决办法是使_idGenerationService.GetNextId
线程安全。您可能需要对作为此方法基础的数据库查询应用适当的锁定。
同时,你的锁定区域太长。您还介绍了插入数据库的操作。这会导致并发性不足,并可能导致分布式死锁。