假设有以下C#POCO:
public class Gift
{
public int Id { get; set; }
public string Owner { get; set; }
}
以下服务:
public class GiftClaimService
{
private ISomeRepository _someRepository; // Going to be injected or whatever
public bool ClaimGift(int giftId, string myName)
{
var gift = _someRepository.Get(giftId);
if (gift != null && gift.Owner == null)
{
gift.Owner = myName;
_someRepository.Save(gift);
return true;
}
return false;
}
}
我希望这很容易解决。任何使用正确ID呼叫“ClaimGift”的用户都可以申请礼品,在这种情况下,他的名字将作为礼品的所有者。使用的存储库超出了范围,如果是数据库等也没关系。
现在这种情况显然会遇到一些并发问题。拥有GiftClaimService
和ISomeRepository
自己实例的两个或多个用户可能会在几乎同一时间尝试申请礼物,从而导致该方法出现问题(例如,多次覆盖所有者,撤回错误的结果或你能想象到的任何结果。)
我想通过创建一个“全局”锁定对象来锁定它,这将阻止其他人(其他线程/实例等)获取同一个锁,仅限于类型(Gift
)和它是Id
。对于不同的ID,这可能会“同时”(虽然不是在同一个实例中)。
提交更改后,锁定将由其所有者释放(通过调用存储库中的Save
)。对于其他人,它的行为与通常的锁一样:处理在此命令处于暂停状态。新方法如下所示:
public bool ClaimGift(int giftId, string myName)
{
var result = false;
// Lock it up (Pseudo-command)... I have no idea how this could work?
lock(typeof(Gift), giftId)
{
var gift = _someRepository.Get(giftId);
if (gift != null && gift.Owner == null)
{
gift.Owner = myName;
_someRepository.Save(gift);
result = true;
}
}
return result;
}
这就是我希望它看起来如何,是否有任何常见的技术/模式如何解决这个问题?
答案 0 :(得分:1)
void Main()
{
var s = new GiftClaimService ();
s.ClaimGift(1,"Me");
}
public class Gift
{
public int Id { get; set; }
public string Owner { get; set; }
}
public interface ISomeRepository
{
Gift Get(int giftId);
void Save(Gift gift);
}
public class GiftClaimService
{
private ISomeRepository _someRepository; // Going to be injected or whatever
public readonly static Dictionary<int, string> _claimsInProgress = new Dictionary<int, string>();
private static readonly object _locker = new object();
public bool ClaimGift(int giftId, string myName)
{
lock (_locker)
{
if ( _claimsInProgress.ContainsKey(giftId))
return false;
}
var gift = _someRepository.Get(giftId);
if (gift == null)
return false; // no such gift
lock (_locker)
{
if ( _claimsInProgress.ContainsKey(giftId))
return false;// someone claimed it just now
_claimsInProgress.Add(giftId, myName);
}
bool retValue;
if (gift.Owner == null)
{
gift.Owner = myName;
_someRepository.Save(gift);
retValue = true;
}
else
retValue = false;
lock (_locker)
{
_claimsInProgress.Remove(giftId);
}
return retValue;
}
}
答案 1 :(得分:0)
如果要处理Web场方案,则必须在数据库级别处理。否则你可以尝试下面的代码:
public class GiftClaimService
{
private static readonly object Lock = new object();
private ISomeRepository _someRepository; // Going to be injected or whatever
public bool ClaimGift(int giftId, string myName)
{
lock (Lock)
{
var gift = _someRepository.Get(giftId);
if (gift != null && gift.Owner == null)
{
gift.Owner = myName;
_someRepository.Save(gift);
return true;
}
return false;
}
}
}