考虑以下 C++11 代码,其中类B
被实例化并由多个线程使用。因为B
修改了共享向量,所以我必须在 ctor 和成员函数 foo 中锁定对它的访问B
。为了初始化成员变量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));
}
};
你能帮我理解为什么后一个例子有效(是因为它不使用相同的互斥锁吗?)?有没有一种安全的方法可以id
在初始化器列表中进行初始化B
而不将其锁定在ctor的主体中?我的要求是id
必须const
的,并且初始化id
发生在初始化列表中。