C++:并发和析构函数

dar*_*mos 5 c++ concurrency destructor critical-section

假设您有一个可以被多个线程访问的对象。临界区用于保护敏感区域。但是析构函数呢?即使我一进入析构函数就进入临界区,一旦调用了析构函数,对象是否已经失效了?

我的思路:假设我进入了析构函数,我必须等待临界区,因为其他线程仍在使用它。一旦他完成,我就可以完成破坏对象。这有意义吗?

bdo*_*lan 3

一般来说,在知道没有其他线程正在使用某个对象之前,不应销毁该对象。时期。

根据您的“思路”考虑这种情况:

  • 线程A:获取对象X引用
  • 线程A:锁定对象X
  • 线程B:获取对象X引用
  • 线程 B:阻塞对象 X 锁
  • 线程A:解锁对象X
  • 线程B:锁定对象X;解锁对象X;销毁对象X

现在考虑如果时间略有不同会发生什么:

  • 线程A:获取对象X引用
  • 线程B:获取对象X引用
  • 线程B:锁定对象X;解锁对象X;销毁对象X
  • 线程 A:锁定对象 X - 崩溃

简而言之,对象销毁必须在对象本身以外的其他地方同步。一种常见的选择是使用引用计数。线程 A 将锁定对象引用本身,防止引用被删除和对象被销毁,直到它设法增加引用计数(保持对象处于活动状态)。然后线程 B 只是清除引用并减少引用计数。您无法预测哪个线程将实际调用析构函数,但无论哪种方式都是安全的。

boost::shared_ptr引用计数模型可以通过使用或轻松实现std::shared_ptrshared_ptr除非所有线程中的所有 s 都已被销毁(或指向其他地方),否则析构函数不会运行,因此在销毁时您知道指向剩余对象的唯一指针是this析构函数本身的指针。

请注意,使用shared_ptr时,重要的是要防止原始对象引用发生更改,直到您可以捕获它的副本。例如:

std::shared_ptr<SomeObject> objref;
Mutex objlock;

void ok1() {
  objlock.lock();
  objref->dosomething(); // ok; reference is locked
  objlock.unlock();
}

void ok2() {
  std::shared_ptr<SomeObject> localref;
  objlock.lock();
  localref = objref;
  objlock.unlock();

  localref->dosomething(); // ok; local reference
}

void notok1() {
  objref->dosomething(); // not ok; reference may be modified
}

void notok2() {
  std::shared_ptr<SomeObject> localref = objref; // not ok; objref may be modified
  localref->dosomething();
}
Run Code Online (Sandbox Code Playgroud)

请注意,同时读取ashared_ptr是安全的,因此如果对您的应用程序有意义,您可以选择使用读写锁。