C# 如何锁定对非性能代码的调用



我在一个名为Loadeable的类中有一个名为Loadeable的锁定对象:

private readonly object _lock;

lock(_lock){ /*...*/ }语句仅在类 Loadable 中使用,以防止死锁。(注意:_lock对象的访问器是私有的)

由于我仍然想从外部运行锁定的代码,因此我将以下方法添加到 Loadable 类中:

public void RunLockedProcedure(Action Procedure) {
   lock(_lock) {
      Procedure();
   }
}

Loadable 还包含一个名为 IsLoaded 的属性和一个名为 Load(int) 的方法,但我不显示它们,因为它是很多代码并且并不重要。但是,请务必注意,IsLoadedLoad(int)访问线程安全数据。

现在想象 Loadable 的子类称为 Item 和 Creation
整数_creationId和以下方法是 Item 的成员:

public Creation LazyGetCreation() {
   int creationIdCopy = 0;
   //The following lambda provides thread-safety inside this instance.
   RunLockedProcedure(() => {
      if(!IsLoaded) {
         throw new InvalidOperationException("A component cannot be loaded if the instance is not loaded");
      } else {
         if(_creationId < 1)
            throw new InvalidOperationException("The loaded Id of the creation component (" + _creationId + ") is less than 1.");
         creationIdCopy = _creationId;
         if(Creation == null)
            Creation = new Creation();
      }
   });
   //We are not in a thread-safe block and thus Creation might already be null again (I had to use the elvis operator)
   Creation?.RunLockedProcedure(() => {
      if(!Creation.IsLoaded)
         Creation.Load(creationIdCopy); //This is a heavy operation and takes a bit time...
   });
}

现在让我解释一下这个问题:
猫王运算符很棒,但对Creation(?).RunLockedProcedure(() => { /*...*/ })的调用实际上应该在上面的线程安全块内执行(它需要加载 item)。
但是后来我遇到了一个问题,即非常重的Creation.Load(creationIdCopy)也会在锁定的块中执行,从而锁定该项目很长一段时间。我正在寻找一种安全调用Creation.RunLockedProcedure(/*...*/)但在非锁定范围内执行() => { /*...*/ }的方法。(必须锁定到创建实例,而不是项目实例)

也许有一个我还不知道的功能,但找到解决方案会很伤人。
谢谢。

线程安全保证可能应该在Load本身处理,而不是在外部处理。否则,您在调用负载的每个位置都需要它。这样的东西可能适合你:

private object _lock = new object();
public void Load(int someID)
{
    if (IsLoaded) //No-locked check, early exit.
        return;
    lock (_lock) //We think it's not loaded, lock to prevent issues and double check
        if (IsLoaded)
            return;
        else
            IsLoaded = true; //Immediately set IsLoaded = true
    try 
    {
        //Do loading stuff
    } catch (Exception ex) {
        lock(_lock)
            IsLoaded = false; //We failed to load, set back to false again
        throw;
    }
}

然后你这样称呼它:

public Creation LazyGetCreation() {
   int creationIdCopy = 0;
   //The following lambda provides thread-safety inside this instance.
   RunLockedProcedure(() => {
      if(!IsLoaded) {
         throw new InvalidOperationException("A component cannot be loaded if the instance is not loaded");
      } else {
         if(_creationId < 1)
            throw new InvalidOperationException("The loaded Id of the creation component (" + _creationId + ") is less than 1.");
         creationIdCopy = _creationId;
         if(Creation == null)
            Creation = new Creation();
      }
   });
   Creation.Load(creationIdCopy);
}

如果您不喜欢立即设置 IsLoaded ,则可以引入IsLoading并在创建对象之前检查两者都为 false,并立即将IsLoading设置为 true。

最新更新