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 [重复]