如果A("whole")有一组B("parts"),如何同步一个"part" B从一个"whole" A到另一个的传输?

If A ("whole") has a set of Bs ("parts"), how to synchronize the transfer of a "part" B from a "whole" A to another?

假设我们有整体-部分对象关系。例如,有一个 Boss 可以拥有多个 Worker。 对于Boss,我们需要以下操作:

我在下面提供了一些 C++ 代码来说明这个想法(跳过了一些功能)。

class Worker {
private:
    Boss *owner = nullptr;
    std::string name;

public:
    Worker(std::string name) : name(name), owner(nullptr) { }

    Boss* getOwner() const {
        return this->owner;
    }

    void setOwner(Boss *owner) {
        this->owner = owner;
    }
};

class Boss {
private:
    std::set<Worker *> workers;
    std::string name;

public:
    Boss(std::string name) : name(name) { }

    ~Boss() {
        for (auto worker : this->workers) {
            delete worker;
        }
        this->workers.clear();
    }

    void addWorker(Worker *worker) {
        this->workers.emplace(worker);
        worker->setOwner(this);
    }

    void transferOwnership(Worker *dummyWorker, Boss *newOwner);
};

int main()
{
    Boss b1{ "boss1" };
    Boss b2{ "boss2" };

    b1.addWorker(new Worker{ "w1" });
    b1.addWorker(new Worker{ "w2" });

    b2.addWorker(new Worker{ "w3" });

    Worker temp{ "w2" };
    b1.transferOwnership(&temp, &b2);

    b1.print(); // boss: boss1 having workers: w1
    b2.print(); // boss: boss2 having workers: w2 w3

    return 0;
}

serial transferOwnership() 函数的实现如下:

void Boss::transferOwnership(Worker *dummyWorker, Boss *newOwner) {
    // find existing worker
    Worker *actualWorker = nullptr;
    for (auto worker : this->workers) {
        if (*worker == *dummyWorker) {
            actualWorker = worker;
        }
    }

    if (nullptr == actualWorker) {
        return;
    }

    // remove existing worker from this
    this->workers.erase(actualWorker);

    // add worker to other (newOwner)
    newOwner->workers.emplace(actualWorker);
    actualWorker->setOwner(newOwner);
}

现在,我们想要并行化这个场景。假设有多个线程可以在任何现有 Boss 对象上执行任何可用操作(addWorker()transferOwnership())。

问题是如何同步代码,以便我们始终在最后获得一致的结果,而不会陷入死锁。

我认为一个解决方案是在 Boss class 中有一个静态互斥变量,在操作开始时锁定它并在结束时结束它。这将保护所有 Boss 个对象的所有 workers 组。

另一种解决方案是为每个 Boss 对象设置一个互斥锁。但是,我认为这里我们可能会遇到死锁,因为在 transferOwnership() 中我们需要 lock/unlock 两个 Boss 的互斥体,我认为在这种情况下我们不能强加特定的锁定顺序。

你能想到other/better个解决方案吗?

静态互斥解决方案有效。但是,如果您确实希望每个对象都有一个互斥锁,那么您的事务需要锁定 2 个老板和 1 个工人(根据您的要求,您可能只需要锁定 2 个老板对象)。

为了避免这种情况下的死锁,只需实现一个函数,根据一些众所周知的预定义顺序锁定 3 个互斥量。它们在内存中的位置可能用于此目的。

你的函数看起来像:

  1. 根据内存地址对 2 或 3 个互斥锁进行排序
  2. 根据该顺序锁定它们(确保您不会两次获得相同的互斥量)

如果您使用的是 C++11,则可以只使用 std::lock(std::mutex... mutexes) 或多或少做同样的事情。

void lock(std::mutex& m1, std::mutex& m2) {
  if (&m1==&m2)
    m1.lock();
  else if (&m1<&m2) {
    m1.lock();
    m2.lock();
  } else {
    m2.lock();
    m1.lock();
  }
}

解锁以相反的顺序完成。