Elo*_*off 5 c++ multithreading undefined-behavior
我memcpy在一个线程中写入内存区域(带有),在另一个线程中将其复制到新位置memcpy。有时,这些操作可能会重叠,从而导致数据争用。具有数据争用的程序会调用未定义的行为,并且无效。
在这种情况下,我会在副本之后检查复制的数据是否有效(实际上没有发生争用。)如果确实发生了争用,我将丢弃复制的数据。但是,对于AFAIK,这并不能让我摆脱UB的束缚。我认为是否使用数据竞争的结果仍然是UB。
现在,我可以memcpy在汇编中编写自己的例程(或仅从libc复制并粘贴该例程),这将避免整个UB问题。汇编不是C ++,汇编中发生的任何事情都不会授予编译器调用鼻恶魔的许可[1]。内联汇编以及外部编译和链接的汇编确实是这样吗?尽管memcpy已经在任何现代libc中进行了组装,但编译器也可以对其进行特殊处理,该编译器通常会进行优化,例如memcpy对已知大小和对齐方式进行小的内联-可能会再次调用鼻恶魔。
我在想什么吗?很难想象编译器会像上帝一样,可以在编译时检测到数据竞争-同时又如此愚蠢,以至于优化器使用它来生成错误代码而不是报告错误代码。但是编译器最近有办法突破这两个限制-因此我觉得有必要在Stack Overflow上寻求建议。
[编辑]由于我对如何进行同步有很多好奇,请允许我解释一下。线程之间共享指向要复制的内存的指针。通过atomic访问load(mo_acquire)。然后将内存复制到新位置。然后是一个LoadLoad barrier,其后是第二个 load(mo_relaxed)指针。如果指针不匹配,则复制的结果将被丢弃,因为在复制过程中可能有另一个线程与此线程竞争。写入内存的线程首先将atomic指针更新为null,store(mo_relaxed)然后是a StoreStore barrier和Racing Memcpy。因此,当两次致电memcpy在不同的线程中可能是数据争用-实际上总是被检测到并且在这种情况下总是丢弃结果。我将此方案称为“读取时复制”,我用它来允许在缓存中的对象被逐出之后但在重新使用内存之前在没有任何互斥量或“强”同步的情况下恢复缓存中的对象。
[1]:我渴望有一个更加文明的时间,即编译器报告UB而不是滥用UB进行优化,这可能与程序员期望的行为相反。
小智 2
同步锁使用与您正在执行的操作非常相似的方法,尽管仅适用于非常少量的内存。如果数据争用发生率较高,同步锁会更快,但如果争用率较低,您的方法实际上可能会更快。
虽然memcpy的结果是未定义的,但这不是未定义的行为,只要您可以检测是否发生了竞争,并知道是否忽略垃圾结果即可。
这听起来并不像是您面临保护违规或类似崩溃错误的风险;我对 memcpy 的使用还不够多,不知道是否有任何情况可能会在重叠操作期间崩溃,但我不认为应该这样做。
因此,只要行为可以被检测到,如果它以明显优于标准方法的方式满足您的需求,这不一定是坏事。我不建议使用这个“只是因为”,但是如果您需要传统锁无法获得的速度,并且您以通常提供文档的方式非常彻底地记录了定义明确但非标准的行为对于维护来说,这是可以接受的。
至于编译器优化注释,我从未见过编译器依赖未定义行为来优化代码,并且由于 C++ 编译器需要根据 C++ 规范保证特定行为,因此我会立即停止使用任何依赖未定义行为的编译器那个目的。库代码专门记录了不支持也不应该执行跨线程同时读/写操作,因此以这种方式跨线程使用库代码并不符合未定义的行为,而是您自己故意滥用库代码风险,所有明示或暗示的保证均无效。