【问题标题】:InterlockedIncrement vs EnterCriticalSection/counter++/LeaveCriticalSectionInterlockedIncrement 与 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 是错误的,因为我的计算的最终结果(有时)是错误的。对于计数器的具体值,我只是在一个由计数器的值索引的表中写了一个数字。表中的某些条目不写入,而另一些则写入两次,尽管表中的每个条目应该只写入一次。实际上,在stackoverflow.com/questions/8357955/… 运行代码数千次应该会重现问题。

标签: c++ windows multithreading thread-safety


【解决方案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 返回。如果您想查看唯一值,则需要在计算中使用该值。

【讨论】:

    猜你喜欢
    • 2013-09-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-12-02
    • 1970-01-01
    • 2010-09-27
    • 2014-04-22
    • 1970-01-01
    相关资源
    最近更新 更多