功能联锁交换

Posted

技术标签:

【中文标题】功能联锁交换【英文标题】:Function InterlockedExchange 【发布时间】:2014-01-05 15:50:34 【问题描述】:

我正在处理一个在多个线程之间共享的列表。

我相信在这种情况下要获得好的性能,使用InterlockedExchange函数在这个列表中插入数据会很好,但我有一些疑问。

如果一个线程试图通过 InterlockedExchange 读取另一个线程正在写入的变量,会有什么反应?正在读取变量的线程是等待写入完成还是继续运行无法读取变量?

在使用 interlockedechange 写入变量时,是否需要使用 InterlockedExchange 读取变量?

如何测试这个函数来尝试知道线程间对共享变量的多次访问会有什么反应?

【问题讨论】:

该列表将包含什么样的数据? 记录一些项目 如果我使用互斥锁,性能会很低,因为多个线程正在访问这个列表。 -1 代码在哪里?我对您的代码的正确性表示严重怀疑。 无法测试线程代码以确保其按预期工作。通过测试,您只能在观察到故障时证明它不起作用 【参考方案1】:

简短的回答是InterlockedExchange 不太可能帮助您。但重要的是要了解为什么会出现这种情况,这样您才能更好地了解可能有帮助的情况。

为什么并发读写有问题?

首先请注意,任意数量的线程都可以读取相同的数据,而无需担心。我们只有在一个线程能够并发写入时才开始担心。现在,写作本身并没有什么固有的风险。当即使是最轻微的决策逻辑应用于并发使用的共享数据时,问题也会出现。

例如,您可能有一个列表。一个线程(作者)从列表中删除一个元素,而其他线程正在读取列表中的元素。如果您对操作的时间不满意,读者可能会在作者删除它的同时确认一个元素存在。然后,当阅读器尝试使用 不再有效 元素时,充其量是访问冲突,最坏的情况是数据损坏。

如何保护数据

保护数据的最简单方法(无需架构更改)通常是引入一些表单锁定/阻塞机制。在一个线程开始一项关键操作之前,它会说:“我正忙于共享数据,所有其他线程必须等待直到我完成才能使用数据。”

请注意,这种简单化的方法确实会带来其他问题:

如果许多线程将大部分时间都花在等待或“争夺锁”上,性能可能会显着降低。 锁管理问题会导致两个线程相互阻塞,从而导致系统挂起(称为死锁)。

(注意:有许多替代方法可以保护您的数据,但这超出了本文的范围。)

互锁交换

这个例程和它的其他Interlocked*** 兄弟提供了一个简化的锁定机制,覆盖了基本的、通用的 写操作的步骤。 注意 如上所述,它仍然是一种锁定机制,并且存在大部分问题。

InterlockedExchange保护的两步操作是:

读取前一个值并 写入新值。

这个可能需要保护的原因是,如果两个线程同时交换一个共享值,就有可能出现不一致的行为。

例如给定初始值为 A。线程 #1 交换将值设置为 B。线程 #2 交换将值设置为 C。如果线程同时运行与 #1 处理的时间比 #2 稍早,则有 2 种可能的结果。

使用锁定 #2 将被阻塞,直到 #1 完成,最终结果将始终为:值设置为 C。线程 #1 引用了 A。线程 #2 引用了 B。 如果没有锁定,我们通常会得到与上面相同的结果,但也有我们没有的可能性。最终结果可能是:值设置为 C。线程 #1 引用了 A。线程 #2 引用了 A <-- Discrepancy

这并不总是一个问题,但在某些情况下可能会。在这种情况下,InterlockedExchange 是最简单的基于锁的保护机制。

为什么 InterlockedExchange 不太适合您

您说您的数据在列表中,但没有说明是哪种列表;所以我假设一个标准的德尔福TList。将项目插入列表中并不是一个简单内部操作。

会自动维护一个内部计数器。 可能需要移动现有项目。 需要检查列表的当前容量,并且可能需要增加。

注意!注意:即使您使用的列表数据结构本身可以从InterlockedExchange 中受益,还有其他问题 你需要注意。

您在这里有两组组数据。有列表结构的内部数据(你可能不这么认为,但它就在那里)。然后是您的实际记录数据。

即使在保护了您的列表结构之后,如果您有任何线程可以同时更新您的记录,那么您就有一个潜在的问题。您需要保护您的数据 - 而不仅仅是将数据添加到集合中。这意味着您可能需要一个跨越两个数据集的锁定机制;并且任何基本的、通用的Interlocked*** 例程都不可能实现这一点。

【讨论】:

【参考方案2】:

如果一个线程试图读取一个正在写入的变量 InterlockedExchange 的另一个线程,将是什么 反应?正在读取变量的线程是等待写入完成还是继续运行无法读取变量?

阅读线程将阅读:

写操作执行前存储在内存中的值,或者 执行写操作后存储的值。

事实上,这对于普通的读/写争用是正确的,也就是说,如果你没有使用原子函数。机器字大小(或更小)数据的内存访问是原子的。那就是保证您没有部分阅读或权利。由于您使用的是 InterlockedExchange,因此您的变量必须是对齐的。因此,不可能有部分读取或写入,因此不会撕裂。

现在,如果您的变量未对齐,那么数据竞争可能导致读取线程接收部分预写值和部分写后值。这就是所谓的撕裂。

使用InterlockedExchange写入变量时,是否需要使用InterlockedExchange读取变量?

没有。事实上那是行不通的。因为 InterlockedExchange 修改了变量。而读取操作则不会。使用普通内存读取读取值。这是唯一的方法。当然,您与写入线程存在数据竞争,但这是不可避免的。


我非常怀疑您的代码是否正确实现了无锁容器。将项目插入无锁容器中并不容易实现。事实上,无锁容器非常难以实现。每当您改变容器时,您需要的不仅仅是调用 InterlockedExchange。

【讨论】:

以上是关于功能联锁交换的主要内容,如果未能解决你的问题,请参考以下文章

WaitForSingleObject 与联锁*

详解核心交换机的TRUNK功能(2)

cisco 三层交换机开启路由功能的命令。

交换机为啥要买license?

主要的交换机协议都有哪些

实验四 交换机SPAN功能配置 (交换与路由技术)