CPP compare_exchange_strong虚假失败?



所以我对CPP很陌生,我试图为我正在开发的一个小项目实现一个资源池(SQLITE连接)。

问题是我有一个列表(向量),其中包含在程序开始时创建的对象,这些对象具有给定的连接及其可用性(atomic_bool)。如果我的程序从此池请求连接,则会执行以下函数:

gardener_db &connection_pool_inner::get_connection() {
bool expected = false;
for(auto & pair : pool) {
std::atomic_bool &ref = pair->get_is_busy_ref();
if (ref.compare_exchange_strong(expected, true)) {
return pair->get_conn();
}//if false it means that its busy
}
std::lock_guard<std::mutex> guard(vector_mutex);//increment size
std::atomic_bool t = true;
pool.emplace_back(std::make_shared<pool_elem>());
pool.back()->get_is_busy_ref().store(t);// because we are giving the resource to the caller
return pool.back()->get_conn();
}

所以我做了一个简单的测试,看看我的矢量是否在调整大小:

constexpr unsigned int CONNECTION_POOL_START_SIZE = 20;
TEST(testConnPool, regrow) {
auto saturation = std::vector<connection_handler>();
ASSERT_EQ(saturation.size(), 0);
for(int i = 0; i < CONNECTION_POOL_START_SIZE; i++) {
saturation.emplace_back();
}
auto ptr = connection_pool_inner::instance();
auto size = ptr->get_pool().size();
//it  should be full at this point
ASSERT_EQ(size, CONNECTION_POOL_START_SIZE);
}

问题是我得到 22 作为我的尺寸,而不是我所期望的 20。

我能够将问题缩小到 compare_exchage_strong(),但是,我的理解是"强"变体不会失败。所以我调试了它,它总是我的向量的第三个元素被跳过(即使在单个线程上工作时)。

我已经在不同的计算机(和架构)上测试了同样的东西,但同样的问题仍然存在,所以我猜问题是逻辑。

对正在发生的事情有什么想法吗?

地雷危险教育

#include <mutex>
#include <atomic>
#include <iostream>
#include <vector>
#include <memory>
class foo {
std::atomic_bool inner;
public:
explicit foo(): inner(false){};
std::atomic_bool &get(){return inner;}
};
std::mutex vector_mutex;
std::vector<std::shared_ptr<foo>> resources;
void get_resource() {
bool expected = false;
for(auto & rsc : resources) {
if (rsc->get().compare_exchange_strong(expected, true)) {
return;
}
}
std::lock_guard<std::mutex> guard(vector_mutex);//increment size
resources.emplace_back(std::make_shared<foo>());
}
int main() {
std::vector<std::shared_ptr<foo>> *local = &resources;
for(int i = 0; i < 20; i++) {
resources.emplace_back(std::make_shared<foo>());
}
for(int i = 0; i < 20; i++) {
get_resource();
}
std::cout << resources.size();
}

代码会导致多线程环境中出现未定义的行为。

当循环for(auto & pair : pool)在一个线程中运行时,pool.emplace_back(std::make_shared<pool_elem>())在另一个线程中会使在后台运行循环中使用的pool迭代器失效。

循环中有错误。 std::atomic::compare_exchange_strong:

<...>将存储在*this中的实际值加载到expected(执行加载操作)。

让我们向量busy成为繁忙状态的条件名称。 第一个get_connection()调用会导致向量busy{ true, false, ... false }

第二个get_connection()调用:

  1. expected = false;
  2. busy[0]true不等于expected,它就会得到expected = true;
  3. busy[1]false不等于更新的expected,它会得到expected = false;
  4. busy[2]false等于更新的expected,导致向量busy{ true, false, true, false, ... false }

进一步的 8 次get_connection()调用导致向量busy{ (true, false) * 10 }

第 11 次和第 12 次get_connection()调用增加了几个true,导致向量busy{ (true, false) * 10, true, true },大小为 22。

其他get_connection()调用不再修改向量:

  1. <...>
  2. busy[10]true不等于expected,它就会得到expected = true;
  3. busy[11]true等于更新的expected,返回。

修复

// bool expected = false;  ───────┐
//                                │
for(auto & pair : pool) {   //    │
bool expected = false;  // ◄──┘
std::atomic_bool &ref = pair->get_is_busy_ref();
if (ref.compare_exchange_strong(expected, true)) {
return pair->get_conn();
}//if false it means that its busy
}

相关内容

  • 没有找到相关文章

最新更新