繁体   English   中英

C++ 无锁线程问题 - 多个线程迭代连续数组但从不访问相同的成员数据?

[英]C++ Lockless Threading Question - Multiple threads iterating over a contiguous array but never accessing same member data?

在我的 C++ 游戏引擎中,我有一个利用工作线程执行各种任务的作业系统。 线程关联到每个可用的核心。 最近,我一直在尝试通过最大化 CPU 利用率来优化我的一些系统管道。 这是一些示例伪代码。 它不是一个精确的复制品,但情况相似。

struct entityState {
  uint8 * byteBuffer; // Serialized binary data for the Entity
  uint8 * compressedData; // Compressed version of Entity data
  uint64  guid; // Unique ID
  gameTimeMS lastUpdated; // last time buffer was updated in milliseconds
  uint32 numUpdates; // Count of the number of updates
  uint32 numTimesAckedOverNetwork; // How many times client acked the data
  const char * typeData; // Type data in place of RTT
  bool markedForDelete; // Whether this object should be deleted next frame
  const char * debugData; // In debug configs, store meta data 
  // More member data but the point is made
};

// For examples sake, I have a contiguous array of entityState data
List< entityState * > entityStateList;
PopulateListWithEntityStateData(); // ~20,000 entityState ptrs on average
SortEntityStateList();
// Fire off 5 jobs each with their own worker thread
StartEntityStateJobs();

然后,我有 5 个作业同时在此列表上运行,没有MutexesCritical Sections 每个作业 function 基于一个条件(例如 guid)或只是线性搜索通过二分搜索访问数组。 这是问题所在。 没有任何工作职能修改 entityStateList 中entityState ptrs 的相同成员数据 但是,由于二分搜索与线性搜索存在冲突,它们可以遵循相同的 entityState ptr。 但是,我再说一遍,他们从不同时修改相同的成员数据。 没有成员数据ptrs 在每个线程上同时被取消引用。

我已经用单元测试运行了这个模拟并且没有遇到任何问题。 但是,我有一些程序员朋友说,当取消引用同一个 entityStatePtr 时,这将导致线程暂停和恢复的未定义行为的可能性非常小。

我听说的另一点是此设置起作用的原因是 entityState 结构大小不适合缓存行并最终划分数据获取,由于结构本身,它本身充当数据保护数据被分成不同的缓存行。 澄清一下,假设上半部分适合一个缓存行,下半部分适合另一个缓存行,并且作业函数仅对 entityState ptr 的一个数据成员进行操作,并且大部分时间它恰好位于不同的缓存行上。 我不对成员数据使用任何原子修饰符或操作,因为没有作业涉及相同的成员数据。

最后,我也有一些程序员朋友说这是完全线程安全的。

尽管如此,我有三种不同的陈述,而且我对多线程的低级知识缺乏足够的知识来确定哪个是正确的。

问题是......是否有可能在“x”次中发生一次超低崩溃? 即使是 1/100 万也是不可接受的。 这是一种安全、无锁的线程机制,可以在列表上并行执行多个操作吗? 尝试忽略示例数据的琐碎性。 在我的引擎示例中,它要复杂得多。 此代码可以在多个操作系统上运行,例如 PC、Linux 和控制台。 它尚未崩溃,但曝光和测试是有限的。 我承认我不是低级专家,但这节省了宝贵的表演时间。 那么,我是在等着撞上地雷还是这样安全? 编译器为 gcc 版本 C++11。 另外,请避免局部性的性能话题,除非它与线程和/或线程安全有关。 我知道缓存未命中很糟糕。

问题- 线程是否安全? 如果是或否,请尽可能详细解释原因。 我想加强我的低级知识。

@walnut 已经详细解释了“保证访问数组的不同元素不会导致数据竞争”。

但是,您提到您有多个更新 entityState 的作业函数,并且这些函数由一些作业链 object 排序。 您没有 go 详细介绍此作业链是如何实现的,但您必须确保它在不同的工作职能之间建立适当的先发生关系,否则您确实会在entiyState 成员上发生数据竞争。

我也同意@rustyx - 使用 ThreadSanitizer 运行您的代码。 它有助于揭示许多线程问题,包括数据竞争。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM