gpt4 book ai didi

c++ - 使用回调正确清理父项和子项 (C++)

转载 作者:塔克拉玛干 更新时间:2023-11-03 00:31:55 26 4
gpt4 key购买 nike

这个设计问题反复出现,我仍然没有好的解决方案。它可能会变成一种设计模式;)只是,它似乎非常特定于 C++(缺乏垃圾收集)。无论如何,这是问题所在:

我们有一个保留对子对象的引用的父对象。 parent 的状态取决于(某些聚合)其 child 的状态。为了通知其子级的状态变化,它向他们传递对自身的引用。 (在另一个变体中,它向它们传递一个回调,子级可以调用该回调来通知父级。此回调是一个保留对父级的引用的闭包。)该应用程序是大量多线程的。现在,这个设置是一个充满潜在竞争条件和死锁的马蜂窝。要理解原因,这里有一个简单的实现:

class Parent {
public:
Parent() {
children_["apple"].reset(new Child("apple", this));
children_["peach"].reset(new Child("peach", this));
}

~Parent() {
}

void ChildDone(const string& child) {
cout << "Child is DONE: " << child << endl;
}

private:
map<string, linked_ptr<Child> > children;
};

class Child {
public:
Child(const string& name, Parent* parent)
: name_(name), parent_(parent), done_(false) {}

Foo(int guess) {
if (guess == 42) done_ = true;
parent->ChildDone(name_);
}

private:
const string name_;
Parent* parent_;
bool done_;
};

潜在问题:

  • 在销毁父级期间,它必须留意来自其子级的持续回调。特别是如果这些回调是在单独的线程中触发的。如果不是,它可能会在调用回调时消失。
  • 如果父项和子项中都有锁(很可能在多线程的非平凡应用程序中),锁定顺序就会成为一个问题:父项调用子项的方法,而子项又依次调用经历状态转换并尝试通知父级:死锁。
  • 如果子级试图从其析构函数通知父级,则在构造函数外添加/删除子级可能会成为一个问题。父级必须持有锁才能修改子级的映射,但子级正在尝试对父级进行回调。

    我只触及了表面,但可以想到其他潜在问题。

    我正在寻找的是关于如何在面对线程、锁和动态添加/删除子项时处理父项的干净破坏的一些建议。如果有人提出了在多线程部署下健壮的优雅解决方案,请分享。这里的关键词是健壮:很容易设计一个带有一些巨大警告的结构(子从不调用父,父从不调用子,没有单独的回调线程等),挑战在于对程序员施加尽可能少的限制。

  • 最佳答案

    通常,多线程问题的很大一部分是未能正确分离处理(工作线程,即子线程)和状态。锁定应该通过线程安全数据结构而不是线程本身来完成。消息队列、状态机和其他此类工具旨在允许您以独立于用于更新它们的进程的受控方式管理此类数据。您几乎总是可以返工此类生命周期管理问题,使其成为(线程安全的)数据管理问题。父线程可以被认为是状态的所有者,所有线程都更新状态本身。管理对象生命周期的引用计数也是一种常见的范例。

    关于c++ - 使用回调正确清理父项和子项 (C++),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5400482/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com