当结构数组在主线程中填充数据时,从结构数组的低索引元素读取是否线程安全



原始问题:

我得到了一个结构数组,并在一个单独的线程中填充它,同时在主线程中读取:

struct DataModel MyData[1024];
struct DataModel
{
bool IsFilled;
float a;
float b;
}
  • 我有一个线程,它正在填充从0索引到最后一个索引的Mydata数组(在上面是1024)。

  • 然后我从填充线程中得到最后一个填充的结构索引。

  • 然后我尝试读取元素的值,其中一个索引比填充的索引低。

  • 让我们假设,当第500个元素被填充时,我从MyData数组的499个元素中读取值,因此我确保我没有读取正在写入的数组元素。

Q1:这个线程安全吗?

Q2:是否有可能出现未定义的行为或误读价值?


进一步编辑:

这个问题被错误地编辑以添加更多细节,这就是为什么它在答案上引入了不一致性,所以我将之前的编辑分开,以提高答案和公认答案的一致性。

编辑1:这是一个可能实施的建议。虽然它可能会显示错误的结果,但我只是想询问线程安全性和未定义的行为,下面的解决方案可能会显示各种结果,但是我试图首先询问线程安全。

std::atomic<int> FilledIndex;
void FillingMyData(struct DataModel myData[])
{
for(size_t i = 0; i < 1024; i++)
{
myData[i].a = rand();
myData[i].b = rand();
myData[i].IsFilled = true;
FilledIndex = i;
}
}
int main()
{
std::thread ReadThread(FillingMyData, MyData);
while(FilledIndex < 1024)
{
std::cout << MyData[FilledIndex].a;
}
ReadThread.join();
return 0;
}

是的,在同一数组中处理单独的对象是安全的。尽管数组是一个对象,但我们正在处理的是数组中的元素,这些元素是单独的对象。只要您没有读取编写器正在写入的元素,那么就没有数据竞争,并且代码已经定义了行为。您发布的代码确实存在同步问题,但这里的其他答案涵盖了这些问题。

然而,这里可能发生的是所谓的虚假分享。在这些情况下,单独的对象位于内存中的同一缓存行中。当内核/线程上更改一个对象时,该缓存行将被标记为已更改。这意味着另一个核心/线程必须重新同步该行才能引入任何更改,这意味着两个核心/螺纹不能同时运行。这只是一个性能惩罚,程序仍然会给出正确的结果,只是速度会慢一些。

代码有一个数据竞赛,并且将永远循环。

发生数据竞争是因为FilledIndex的初始值是0,所以,第一次通过循环时,您正在从正在写入的同一索引中读取数据(因为i == 0)。

循环将永远不会结束,因为i将永远不会达到终止值-循环将在将FilledIndex设置为1024之前退出。

编写的代码不一定是安全的,可能没有任何用处。

  • Filled index的初始值为零,因此它可以在写入之前从索引零读取数据,包括可能部分写入的值。您可能希望将其设置为-1,并等待其>=0后再输出
  • 没有什么可以阻止主线程无限期地执行,永远在零输出相同的值——这取决于调度程序;也没有什么可以阻止主线程多次输出给定索引处的值。您可能希望从零到填充索引进行计数,而不是输出填充索引处的值

上述问题可能意味着您选择了不同的方式将填充索引的值传达回主线程。

我认为这段代码肯定不是线程安全的。

首先,变量FilledIndex没有初始化:正如cplusplus.com所说,如果你不向构造函数提交任何值,原子变量将处于未初始化状态。这可能会导致意外行为。

另一个问题是主线程中的退出条件,因为ReadThread中的for语句循环到1023,所以FilledIndex永远不会假设值1024,主线程永远不会退出。

但主要问题是线程调度的不可预测性:是什么确保ReadThread在主线程之后执行?没有什么

因此,您不能确定您正在对数组的所有值进行循环。事实上,如果您尝试多次执行程序,您会看到每次的输出都是不同的,并且会打印出不同的数组值。

例如,如果我们将ReadThread命名为T,将主线程命名为M并将数组命名为
A。这些都是可能的调度(假设语义大小为5的
A):

  • T T M T输出为A[2]
  • M M T M T输出为A[0]A[0]A[1]

实际上,您正在打印A[FilledIndex],并且无法预测FilledIndex将如何更新,因为这取决于线程调度。

我希望你能理解我想说的话。对于任何问题或澄清,显然我在这里!我会尽快回复的!

最新更新