c++ lambda捕獲this 導致多線程下類釋放后還在使用的錯誤


c++的lambda 可以捕獲this指針,使lambda可以在自定義的function內使用類的成員函數,這是因為捕獲this后隱式的在成員變量前加了this

但是需要注意的是,這里捕獲this,不是以一種拷貝的方式,更像是一種引用(或者別名,描述可能不准確),當在外面這個類的生命周期結束時,lambda內部還在調用這個類的成員函數,那么就會出錯

我遇到的問題是 捕獲了類A的this,對A的一個shared_ptr進行操作。偶然會出現shared_ptr的內部基類spt_count_base的報錯。看了下這個shared_ptr 的use_count和weak_count都是0,

這就非常奇怪。shared_ptr計數是線程安全的,(但是實際指向對象和計數不是原子操作),並且訪問這個sptr也加了鎖,為什么會出現被釋放了的情況。

大概再說下情景

class A

{

  shared_ptr<B> sptr;

  sptr.func = [this](){ do something};

}

class B

{

  shared_ptr<map> sptrMap;

  func()

  {

      //概率崩潰
             sptrMap.erase();

  }

}

 

當A被析構后,B注冊的回調被另一個線程調用了func();這時可能會出現A開始析構,剛好析構到B,並且B的sptrMap已經析構時,出現崩潰。但這個順序不是確定的無法保證,所以也不一定必出現這種現象。

防止這種現象可以加個判斷,在sptrMAp不為空時不進行析構。或者用weak_ptr.在捕獲this前,用 weak_ptr p = std::shared_from_this;然后在lambda最開始用p.lock判斷A是否釋放

通過這個了解到了lambda捕獲的一個坑,及類析構的順序及析構時線程安全的保護

另附實際代碼

using namespace std;

struct Foo {
  std::unique_ptr<int> p;
  std::function<void()> f() {
    p.reset(new int(1));
    return [=] { cout << *p << endl; };
  }
};

int main() {
  auto foo = new Foo();
  auto f = foo->f();
  delete foo;
  f();
}
運行結果為0而非1,而且這里輸出0是未定義行為,因為訪問的實際上是被回收的空間,只是因為編譯器的delete並沒有對回收的空間做額外的操作,所以p指向的仍然是原來那塊,只不過那塊已經被unique_ptr的析構函數自動清除了,只不過將清除的部分全部置為0而已。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM