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

时间:2016-03-07 00:04:14

标签: c# thread-safety locking

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

private readonly object _lock;

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

由于我仍想从外部运行锁定代码,我将以下方法添加到可加载类:

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

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

现在想象一下名为Item and Creation 的 Loadable的子类。
整数_creationId和以下方法是

的成员
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...
   });
}

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

也许有一个我还不知道的功能,但找到解决方案会很糟糕。
非常感谢你。

1 个答案:

答案 0 :(得分:0)

线程安全保证应该在Load本身处理,而不是在外部处理。否则,您需要在每个调用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。 / p>