4

我写了一些线程代码,似乎是一个不正确的假设,即整数是线程安全的。现在看来,尽管它们是,但我对它们的使用不是线程安全的。我使用全局整数 ThreadCount 来保存线程数。在线程创建期间,我增加了 ThreadCount。在线程销毁期间,我将其递减。在创建完所有线程后,我等待它们完成(ThreadCount 应降至 0),然后编写我的最终报告并退出。

但有时(5%)我永远不会达到 0,即使对我的日志的事后检查表明所有线程都运行并完成了。所以所有迹象都表明 ThreadCount 被践踏了。我一直在告诉自己这是不可能的,因为它是一个整数,而我只是使用 inc/dec。

这里有一些相关的代码:

var  // global
  ThreadCount : integer;            // Number of active threads
...

constructor TTiesUpsertThread.Create(const CmdStr : string);
begin
  inherited create(false);
  Self.FreeOnTerminate := true;
...
  Inc(ThreadCount);      // Number of threads created.  Used for throttling.
end;

destructor TTiesUpsertThread.Destroy;
begin
  inherited destroy;
  Dec(ThreadCount);     // When it reaches 0, the overall job is done.
end;

...
//down at the end of the main routine:

    while (ThreadCount > 0) do   // Sometimes this doesn't ever end.  
    begin
      SpinWheels('.'); // sleeps for 1000ms and writes dots... to console 
    end;

我认为我的问题在于 inc/dec。我认为我遇到了两个或多个 dec() 同时命中并且都读取相同值的冲突,因此它们将其替换为相同的值。例:ThreadCount = 5,两个线程同时结束,都读到5,换成4。但是新的值应该是3。

这在我们的测试环境中永远不会遇到麻烦(不同的硬件、拓扑、负载等),所以在我尝试将这个解决方案“推销”给业务部门之前,我正在寻找这可能是问题的确认。

如果这是我的问题,我是否使用关键选择来保护 inc/dec?
谢谢参观。

4

1 回答 1

7

如果多个线程在没有保护的情况下修改变量,那么是的,您有数据竞争。如果两个线程试图在同一个实例上增加或减少,那么会发生什么:

  1. 变量被读入寄存器。
  2. 修改是在寄存器中进行的。
  3. 新值被写回变量。

读/修改/写不是原子的。如果您有两个线程同时执行,那么您就有规范数据竞争。

  • 线程 1 读取值,N 说。
  • 线程 2 读取该值,与线程 1 N 读取的值相同。
  • 线程 1 将 N+1 写入变量。
  • 线程 2 将 N+1 写入变量。

而不是变量被增加两次,它只增加一次。

在这种情况下,不需要完整的临界区。用于InterlockedIncrement执行无锁、线程安全的修改。

于 2013-06-19T14:03:53.710 回答