gpt4 book ai didi

c++ - 以线程安全的方式使用回调设置类成员

转载 作者:搜寻专家 更新时间:2023-10-31 02:12:52 24 4
gpt4 key购买 nike

我有下面的类,它允许通过昂贵的计算来设置一个值,我们可以假装计算是在其他线程中异步发生的。该类还有一个获取当前值的方法,如果需要,可能会同步进行该计算:

class Example {
public:
Example() : x_(0) {}
void Set(int x) {
SomeOtherFunc(x, callback_, false);
}

void Finished(int y) {
x_ = y;
}

const int Get() {
if (!x_) {
SomeOtherFunc(1, callback_, true);
}
return x_;
}

private:
int x_;
std::function<void(int)> callback_ =
std::bind(&Example::Finished, this, std::placeholders::_1);
};

让我们假设这个函数:

void SomeOtherFunc(int x, const std::function<void(int)>& func,
bool blocking) {
// Do something expensive, in another thread, possibly blocking.
func(x * 2);
}

这是我想要的方式:

Example e1, e2;
e1.Set(5);
// "10 2"
std::cout << e1.Get() << " " << e2.Get();

我担心的是以下情况:

Example e;
e.Set(10); // Takes a while
e.Get(); // Need the value now

我的问题:

  1. Example 类是线程安全的吗?如果不是,那怎么可能呢?看起来锁可以工作,但可能有点矫枉过正。
  2. 由于 SomeOtherFunc 中的工作成本很高,我只想调用该函数一次。在类中有一个标志,每当调用时都设置为 true,并在调用之前进行检查,这似乎是合理的(但它是线程安全的吗?)。问题是,如果我调用 Set 然后立即调用 Get,我希望 Get 返回 x_ 的“最终”值。我怎样才能确保这一点?也就是说,如果设置了该标志,我如何让 Get 以线程安全的方式等待回调完成?

最佳答案

您的 Example 类不是线程安全的,因为可以同时修改整数 x_,这可能会导致未定义的行为(数据竞争)。此外,您的昂贵计算可能会执行多次,因为您在 Get() 中存在竞争条件,方法是检查 x_ 然后调用将设置它的函数。
您需要一种机制来保证 x_ 的值将在完全线程安全的情况下精确计算一次(Set()Get()可以同时调用)。

标准库提供了一种机制来准确解决该问题,call_once(),旨在实现一次性事件,同时共享数据在线程之间正确同步。你可以像这样使用它:

#include <mutex>

class Example {
public:
...

void Set(int x) {
std::call_once(flag, SomeOtherFunc, x, callback_, false);
}

...

const int Get() {
std::call_once(flag, SomeOtherFunc, 1, callback_, true);
return x_;
}

private:
std::once_flag flag;
...
};

这也处理了 Get() 在您的回调处理结果时必须等待的情况。

请注意,您不再可以(也必须)在 Get() 中检查 x_ 的值,因为这会构成数据竞争(另一个线程可能同时更新 x_)。

另请注意,直接调用回调 Finished() 不是线程安全的。可能最好将其移至 private: 部分。

关于c++ - 以线程安全的方式使用回调设置类成员,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42152195/

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