代码之家  ›  专栏  ›  技术社区  ›  Julio

线程安全延迟获取和释放

  •  2
  • Julio  · 技术社区  · 15 年前

    我遇到了一个麻烦的问题,需要一些建议…

    假设我有一堆小的MyObject,可以构建更大的MyExtendObject。MyExtendObject很大,占用CPU,因此构造很懒惰,我尝试尽快从内存中删除它们:

    MyExtendedObject * MyObject::GetExtentedObject(){
      if(NULL == ext_obj_){
        ext_obj_ = new MyExtendedObject;
      }
      ++ref_;
      return ext_obj_;
    }
    void MyObject::ReleaseExtentedObject(){
      if(0 == (--ref_))
      {
        if(NULL != ext_obj_)
        {
          delete ext_obj_;
          ext_obj_ = NULL;
        }
      }
    }
    

    扩展对象在开始时只构造一次,在最后一个调用程序释放它们时会被销毁。请注意,有些可能会被构造多次,但这在这里不是一个问题。

    现在,这绝对不是线程安全的,所以我做了一个“幼稚的”线程安全实现:

    MyExtendedObject * MyObject::GetExtentedObject(){
      Lock();
      if(NULL == ext_obj_){
        ext_obj_ = new MyExtendedObject;
      }
      ++ref_;
      Unlock();
      return ext_obj_;
    }
    void MyObject::ReleaseExtentedObject(){
      Lock();
      if(0 == (--ref_))
      {
        if(NULL != ext_obj_)
        {
          delete ext_obj_;
          ext_obj_ = NULL;
        }
      }
      Unlock();
    }
    

    这是更好的,但现在我花了一些不可忽视的时间锁定和解锁…

    我有种感觉,只有在构造或破坏时,我们才能支付锁/解锁。

    我想出了这个解决方案:

    MyExtendedObject * MyObject::GetExtentedObject(){
      long addref = InterlockedCompareExchange(&ref_, 0, 0);
      long result;
      do{
        result = addref + 2;
      } while ((result-2) != (addref = InterlockedCompareExchange(&ref_, result, addref)));
      if(0 == (result&1)){
        Lock();
        if(NULL == ext_obj_){
          ext_obj_ = new MyExtendedObject;
          InterlockedIncrement(&ref_);
        }
        Unlock();
      }
      return ext_obj_;
    }
    void MyObject::ReleaseExtentedObject(){
      long release = InterlockedCompareExchange(&ref_, 0, 0);
      long result = 0;
      do{
        result = release - 2;
      } while ((result+2) != (release = InterlockedCompareExchange(&ref_, result, release)));
      if(1 == result)
      {
        Lock();
        if(1 == InterlockedCompareExchange((long*)&ref_, 0, 1))
        {
          if(NULL != ext_obj_)
          {
            delete ext_obj_;
            ext_obj_ = NULL;
          }
        }
        Unlock();
      }
    }
    

    一些解释:

    • 我不能用助推器。我想去,但真的不行。

    • 我只是故意使用CompareExchange和Incr/Decr。不要问。

    • 我使用引用的第一位存储构造状态(构造/未构造)和其他位用于引用计数。这是我发现的唯一一种通过原子操作同时管理2个变量(引用计数和构造状态)的方法。

    现在有些问题:

    • 你认为它是100%防弹的吗?

    • 你知道更好的解决办法吗?

    编辑:有些人建议使用共享资源。一个共享的工作解决方案!请注意,我需要:懒惰的建设和破坏,当没有人再使用它。

    2 回复  |  直到 15 年前
        1
  •  1
  •   Community Mohan Dere    9 年前

    shared_ptr< MyExtendedObject > MyObject::GetExtentedObject(){
      RIIALock lock( my_CCriticalSection_instance );
      shared_ptr< MyExtendedObject > spObject = my_weak_ptr.lock();
      if (spObject) { return spObject; }
    
      shared_ptr< MyExtendedObject > spObject = make_shared< MyExtendedObject >();
      my_weak_ptr = spObject;
      return spObject;
    }
    

    Boost weak_ptr's in a multi-threaded program to implement a resource pool

        2
  •  0
  •   Steve Townsend    15 年前

    boost::shared_ptr

    boost::shared_ptr<MyExtendedObject>

    shared_ptr

    shared_ptr<MyExtendedObject> master(new MyExtendedObject);
    

    master delete MyExendedObject