Thread safety of static variable initialization
我想确定以下代码中具有不同参数类型的2个对Get()的调用是否是线程安全的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| struct MethodTypeIndex
{
template <typename T>
static size_t Get(T)
{
static size_t index = NextIndex();
return index;
}
private:
static size_t NextIndex()
{
static size_t nextIndex = 0;
return nextIndex++;
}
}; |
一方面,在index初始化期间并根据标准,调用NextIndex():
If control enters the declaration concurrently while the variable is
being initialized, the concurrent execution shall wait for completion
of the initialization.
另一方面,我不知道对NextIndex()的调用是否被视为index初始化的一部分。如果没有,括号初始化有区别吗?
1
| static size_t index{ NextIndex() }; |
或者,如果我不想使nextIndex成为原子,还有其他方法可以使其成为线程安全吗?
- 该=右侧的完整表达式为"初始化的一部分"。包括函数调用。
-
NextIndex()调用是index初始化的一部分。但是,如果使用不同的参数类型调用Get<T>和Get<U>,则会调用Get的两个不同的独立实例,每个实例都有自己的index。 index的这两个独立实例的两个初始化不同步。
-
而且,如果没有模板,代码将是线程安全的,并且在复制分配初始化中对NextIndex()的附加调用不会以任何方式对其进行更改?
-
@IgorTandetnik,但是nextIndex的实例化不会同步吗?
-
@SirGuy nextIndex++是吗?
-
@SirGuy nextIndex = 0是。 nextIndex++不会。那是一场等待中的数据竞赛。
-
@ user2198121您说的这个"副本分配初始化"是什么?什么是"对NextIndex()的附加调用"?我不确定我是否理解这个问题。
-
@IgorTandetnik在此代码中,对Get()的调用是否是线程安全的? pastebin.com/izXnNWJt
-
@ user2198121是的,假设除所示的调用之外,没有对NextIndex()的其他调用,这将是线程安全的。但是请注意,NextIndex()将仅被调用一次,而Get()将始终返回零。这可能与您的想法不完全相同。
-
@IgorTandetnik不,那是我想知道的,谢谢。
-
为什么要使用Standardese进行过早的优化?只需使用std::atomic_size_t。它具有operator++以及所有内容。
-
@StoryTeller在我的项目中,我做到了。但这对我来说很有趣。
-
很高兴知道这样的代码不在生产中。否则,这是一个很好的问题。然后继续。
假设您有两个不同的功能:
1 2 3 4 5 6 7 8
| static size_t get_int() {
static size_t index = NextIndex();
return index;
}
static size_t get_long() {
static size_t index = NextIndex();
return index;
} |
您是否有疑问,从单独的线程调用这两个函数不是线程安全的?显然,对NextIndex的调用引起了数据竞争。
通过实例化模板函数来创建这些函数不会摆脱数据竞争。模板不是代码;它们是用于创建代码的模式。从两个不同的线程调用模板函数的两个不同的实例化(例如,Get<int>()和Get<long>())会产生数据争用。
- 您能否补充一下NextIndex()调用是初始化的一部分,并且对其本身的调用是线程安全的?