线程完成但循环不会结束

时间:2013-06-19 13:55:12

标签: multithreading delphi thread-safety

我用一些似乎不正确的假设编写了一些线程代码,整数是线程安全的。现在似乎虽然它们是,但我对它们的使用并不是线程安全的。我使用全局整数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.

这在我们的测试环境中永远不会遇到麻烦(不同的硬件,拓扑,负载等等),所以在我尝试"出售&之前,我正在寻找确认这可能是问题的原因。 #34;这个业务部门的解决方案。

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

1 个答案:

答案 0 :(得分:7)

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

  1. 将变量读入寄存器。
  2. 修改在登记册中进行。
  3. 新值将写回变量。
  4. 读取/修改/写入不是原子的。如果您有两个线程同时执行,那么您将进行规范数据竞争。

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

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

    在这种情况下,不需要完整的关键部分。使用InterlockedIncrement执行无锁,线程安全修改。