Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/138.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ C++;:并发和析构函数_C++_Concurrency_Destructor_Critical Section - Fatal编程技术网

C++ C++;:并发和析构函数

C++ C++;:并发和析构函数,c++,concurrency,destructor,critical-section,C++,Concurrency,Destructor,Critical Section,假设您有一个可以被多个线程访问的对象。关键部分用于保护敏感区域。但是析构函数呢?即使我一进入析构函数就进入了临界段,一旦调用了析构函数,对象是否已经失效 我的思路是:假设我进入析构函数,我必须等待关键部分,因为其他线程仍在使用它。一旦他完成了,我就可以完成销毁目标。这有意义吗?如果某个对象正在使用中,则应确保在该对象的使用结束之前未调用该对象的析构函数。如果这是你的行为,那么这是一个潜在的问题,它确实需要修复 您应该确保如果一个线程正在销毁您的对象,那么另一个线程不应该调用该对象上的函数,或者第

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


我的思路是:假设我进入析构函数,我必须等待关键部分,因为其他线程仍在使用它。一旦他完成了,我就可以完成销毁目标。这有意义吗?

如果某个对象正在使用中,则应确保在该对象的使用结束之前未调用该对象的析构函数。如果这是你的行为,那么这是一个潜在的问题,它确实需要修复

您应该确保如果一个线程正在销毁您的对象,那么另一个线程不应该调用该对象上的函数,或者第一个线程应该等到第二个线程完成函数调用


是的,即使析构函数也可能需要关键部分来保护更新一些与类本身无关的全局数据。

通常,在知道没有其他线程在使用对象之前,不应该销毁对象。句号

根据您的“思路”,考虑这个场景:

  • 线程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只清除引用并减少引用计数。您无法预测哪个线程将实际调用析构函数,但无论如何都是安全的

参考计数模型可以很容易地用or实现;除非所有线程中的所有
shared_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();
}
std::shared_ptr objref;
互斥对象锁;
void ok1(){
objlock.lock();
objref->dosomething();//确定;引用已锁定
objlock.unlock();
}
void ok2(){
std::shared_ptr localref;
objlock.lock();
localref=objref;
objlock.unlock();
localref->dosomething();//确定;本地引用
}
void notok1(){
objref->dosomething();//不正常;可以修改引用
}
void notok2(){
std::shared_ptr localref=objref;//不正常;可以修改objref
localref->dosomething();
}

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

当一个线程在析构函数中等待CS时,另一个线程可能正在销毁对象,如果CS属于对象,它也将被销毁。因此,这不是一个好的设计。

您绝对需要确保您的对象生命周期小于消费者线程,否则您会遇到一些严重的问题。要么:

  • 使对象的消费者成为子对象,这样他们就不可能存在于对象之外,或者
  • 使用消息传递/代理
    如果您选择后一种方法,我强烈建议您选择0mq。

    是,当您在析构函数中时,对象已经无效

    我使用Destroy()方法进入临界区,然后自行销毁


    是的,这样做很好。如果一个类支持这样的使用,客户端不需要同步销毁;i、 在调用析构函数之前,他们不需要确保对象上的所有其他方法都已完成

    我建议客户不要假设他们可以这样做,除非有明确的文档记录。默认情况下,客户确实有这种负担,尤其是标准库对象(§17.6.4.10/2)

    不过,在某些情况下,这是可以接受的<例如,code>std::condition\u variable的析构函数特别允许在
    ~condition\u variable()
    启动时进行
    condition\u variable::wait()
    方法调用。它只要求客户端在
    ~condition\u variable()启动后不启动wait()调用

    与标准库的大多数其他部分一样,要求客户机同步对析构函数(以及构造函数)的访问可能更简洁。如果可行的话,我建议这样做

    然而,在某些模式下,减轻客户机完全同步销毁的负担可能是有意义的<代码>条件变量的整体模式类似于:考虑使用一个处理可能长时间运行的请求的对象。用户执行以下操作:

  • 构造对象
  • 使对象接收来自其他线程的请求
  • 使对象停止接收请求:此时,一些未完成的请求可能正在进行,但不能调用新的请求
  • 销毁该对象。析构函数将阻塞,直到所有请求都完成为止,否则正在进行的请求可能会被删除