我正在处理许多线程共享的列表。
我相信在这种情况下表现良好,使用InterlockedExchange函数在这个列表中插入数据会很好,但我有些疑惑。
如果一个线程试图读取另一个使用InterlockedExchange的线程正在写入的变量,那会有什么反应?正在读取变量的线程将等待写入完成或将继续运行并且无法读取变量?
在使用interlockedechange写入变量时,是否需要使用InterlockedExchange读取变量?
如何测试此函数以尝试了解对线程之间的共享变量的多重访问会有什么反应?
答案 0 :(得分:4)
简短的回答是InterlockedExchange
不太可能帮助你。但重要的是要理解为什么会这样,这样你就能更好地理解它可能有所帮助的情况。
首先请注意,任何数量的线程都可以同时读取相同的数据而无需担心。只要一个线程能够同时写入,我们就会开始担心。现在,写作本身没有任何内在风险。即使最轻微的决策逻辑应用于同时使用的共享数据,也会出现问题。
例如,您可能有一个列表。一个线程(编写器)从列表中删除一个元素,而其他线程正在读取列表中的元素。如果您不熟悉操作的时间,读者可能会在编写器删除它的同时确认元素是否存在。然后,当读者尝试使用不再有效的元素时,最多会出现访问冲突,最糟糕的情况是数据损坏。
保护数据的最简单方法(没有架构更改)通常是引入一些表单锁定/阻塞机制。在一个线程开始一个关键操作之前,它说:“我忙于共享数据,所有其他线程必须 等待 ,直到我完成,如果他们想要使用数据。“
请注意,简单化方法确实会引入其他问题:
(注意:有许多替代方法可以保护您的数据,但这超出了本文的范围。)
此例程及其他Interlocked***
兄弟程序在基本,通用写入操作的步骤中提供了简化的锁定机制。 注意它仍然是如上所述的锁定机制,并且分享了大部分问题。
InterlockedExchange
保护的两步操作是:
此可能需要保护的原因是,如果两个线程同时交换共享值,则可能会出现行为不一致的情况。
E.g。给定初始值为A.线程#1交换将值设置为B.线程#2交换将值设置为C.如果线程与#1处理同时运行,比#2快一些,则有2个可能的结果。
A <-- Discrepancy
。这并不总是一个问题,但在某些情况下可能会出现问题。在这种情况下,InterlockedExchange
是最简单的基于锁的保护机制。
你说你的数据在列表中,但没有说明什么样的列表;所以我假设一个标准的Delphi TList
。将项目插入列表不是内部的 简单 操作。
NB!注意:即使您使用的列表数据结构本身可以从InterlockedExchange
中受益,也有 还有其他问题 < / strong>你需要注意。
这里有两个数据集。列表结构的内部数据(您可能不会这样认为,但它就在那里)。然后是您实际记录的数据。
即使在保护列表结构之后,如果您有任何可以同时更新记录的线程,您也可能遇到问题。您需要 保护您的数据 - 而不仅仅是将其添加到集合中的行为。这意味着您可能需要一个跨越两个数据集的锁定机制;并且任何基本的,通用的Interlocked***
例程都不可能实现这一点。
答案 1 :(得分:2)
如果线程试图读取正在写入的变量 使用InterlockedExchange的另一个线程,将是什么 反应?正在读取变量的线程将等待写入完成或将继续运行并且无法读取变量?
阅读主题将读取:
事实上,对于普通的读/写争用,即如果你没有使用原子函数。机器字大小(或更小)数据的存储器访问是原子的。那就是保证你不会有部分阅读或权利。必须是您的变量已对齐的情况,因为您使用的是InterlockedExchange。因此,不会有部分读取或写入,因此不会撕裂。
现在,如果您的变量未对齐,则数据争用可能导致读取线程接收预写入值的一部分,以及写入后部分值。这被称为撕裂。
在使用InterlockedExchange写入变量时,是否需要使用InterlockedExchange读取变量?
没有。事实上,这是行不通的。因为InterlockedExchange会修改变量。并且读操作没有。使用普通内存读取读取值。这是唯一的方法。当然,你有一个写作线程的数据竞争,但这是不可避免的。
我严重怀疑您的代码是否正确实现了无锁容器。将项目插入无锁容器中并不容易实现。实际上,无锁容器非常难以实现。每当你改变容器时,你需要的不仅仅是调用InterlockedExchange。