InterlockedIncrement 与 EnterCriticalSection/counter++/LeaveCriticalSection

Posted

技术标签:

【中文标题】InterlockedIncrement 与 EnterCriticalSection/counter++/LeaveCriticalSection【英文标题】:InterlockedIncrement vs EnterCriticalSection/counter++/LeaveCriticalSection 【发布时间】:2011-12-06 00:52:12 【问题描述】:

我有一些多线程代码(参见问题Windows API Thread Pool simple example),我使用计数器来识别线程。

有人建议我在线程的回调函数中使用 InterlockedIncrement 来增加这个计数器。但是,这似乎没有正确锁定变量,因为我遇到了一些并发问题。我通过手动使用临界区替换了 InterlockedIncrement:EnterCriticalSection/counter++/LeaveCriticalSection,现在可以完美运行。

为什么会这样?这两个选项不应该是严格等效的吗? 请注意,我说的是只启动几个(大约 10 个)线程。

【问题讨论】:

以什么方式似乎没有正确锁定变量?您遇到了哪些并发问题? InterlockedIncrement 和朋友不需要锁定。执行单个汇编指令。您能更详细地描述您遇到的问题吗? LukeH :计数器并不总是给出严格递增的连续整数序列。有时,从 0 开始的计数器正在执行:0 1 2 2 4 5... @WhitAngl:你如何检查计数器的值? LukeH:首先我意识到线程 ID 是错误的,因为我的计算的最终结果(有时)是错误的。对于计数器的具体值,我只是在一个由计数器的值索引的表中写了一个数字。表中的某些条目不写入,而另一些则写入两次,尽管表中的每个条目应该只写入一次。实际上,在***.com/questions/8357955/… 运行代码数千次应该会重现问题。 【参考方案1】:

您的代码没有正确使用InterlockedIncrement

InterlockedIncrement(&(thread.threadCount)); 
DWORD tid = (thread.threadCount-1)%thread.size(); 

这将执行thread.threadCount 的原子增量,但不是保存原子增量值,而是忽略它并返回thread.threadCount 变量(同时可能已被另一个线程递增)。

在您的情况下,发生的情况是两个线程几乎同时执行 InterlockedIncrement,将其从 1 增加到 2,然后从 2 增加到 3。两个线程然后读取 thread.threadCount 并返回 3(然后减去 1 得到2) 的最终结果。

正确的代码是

LONG tidUnique = InterlockedIncrement(&(thread.threadCount));
DWORD tid = (tidUnique-1)%thread.size(); 

唯一的递增值由InterlockedIncrement 返回。如果您想查看唯一值,则需要在计算中使用该值。

【讨论】:

以上是关于InterlockedIncrement 与 EnterCriticalSection/counter++/LeaveCriticalSection的主要内容,如果未能解决你的问题,请参考以下文章

无法在 DLL 'kernel32.dll' 中找到名为 'InterlockedIncrement' 的入口点 - VS2005@Win7 64 位

InterlockedIncrement vs InterlockedIncrementAcquire vs InterlockedIncrementNoFence [重复]

InterLockedIncrement and InterLockedDecrement函数原理

线程同步

22.原子操作

联锁函数 - 无法转换参数