【发布时间】:2012-04-18 12:42:09
【问题描述】:
考虑以下 C++11 代码,其中类 B 被实例化并由多个线程使用。因为B 修改了一个共享向量,所以我必须在B 的ctor 和成员函数foo 中锁定对它的访问。为了初始化成员变量id,我使用了一个作为原子变量的计数器,因为我从多个线程访问它。
struct A {
A(size_t id, std::string const& sig) : id{id}, signature{sig} {}
private:
size_t id;
std::string signature;
};
namespace N {
std::atomic<size_t> counter{0};
typedef std::vector<A> As;
std::vector<As> sharedResource;
std::mutex barrier;
struct B {
B() : id(++counter) {
std::lock_guard<std::mutex> lock(barrier);
sharedResource.push_back(As{});
sharedResource[id].push_back(A("B()", id));
}
void foo() {
std::lock_guard<std::mutex> lock(barrier);
sharedResource[id].push_back(A("foo()", id));
}
private:
const size_t id;
};
}
不幸的是,这段代码包含一个竞争条件并且不能像这样工作(有时 ctor 和 foo() 不使用相同的 id)。如果我将 id 的初始化移动到由互斥锁锁定的 ctor 主体,它可以工作:
struct B {
B() {
std::lock_guard<std::mutex> lock(barrier);
id = ++counter; // counter does not have to be an atomic variable and id cannot be const anymore
sharedResource.push_back(As{});
sharedResource[id].push_back(A("B()", id));
}
};
你能帮我理解为什么后一个例子有效(是因为它不使用相同的互斥锁吗?)?有没有一种安全的方法可以在 B 的初始化列表中初始化 id 而不会将其锁定在 ctor 的主体中?我的要求是id 必须是const,并且id 的初始化发生在初始化列表中。
【问题讨论】:
-
您能否发布导致问题的实际代码。您提出的代码没有意义(至少在没有
A的定义的情况下)。例如,您不能简单地访问sharedResource[id],而无需实际调整sharedResource的大小以包含id + 1元素。除非A包含成员函数push_back,否则代码甚至不应该编译。 -
@JamesKanze 为什么
A需要push_back成员?我只看到一个(const char*,size_t)构造函数和一个移动/复制构造函数在使用中。 OP:如果可能,请将其设为SSCCE -
@je4d :
sharedResource是一个std::vector<A>,所以sharedResource[id]返回一个A&和sharedResource[id].push_back(...)因此调用A::push_back。 -
@ildjarn 啊,是的,我扫描它的速度太快了,并认为因为
A被推送,它没有被推送到A,因为这对push_back的常规语义。我希望代码不是 OP 实际打算写的。 -
使用 push_back 或按索引访问。两者放在一起没有任何意义。
标签: c++ thread-safety c++11 mutex atomic