【问题标题】:Threads complete but loop doesn't end线程完成但循环没有结束
【发布时间】:2013-06-19 13:55:11
【问题描述】:

我写了一些线程代码,似乎是一个错误的假设,即整数是线程安全的。现在看来,尽管它们是,但我对它们的使用不是线程安全的。我使用全局整数 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?
感谢您的观看。

【问题讨论】:

  • 尽管我绝对没有任何事实可以支持它,但我相信IncDec 不是原子的,尤其是当您进行范围检查时。您可能想查看 WinAPI 函数 InterlockedIncrement(..) 或使用 TCriticalSection 或(可能更快)TMultiReadExclusiveWriteSynchronizer
  • 要阻塞直到所有线程都完成,您可以使用WaitForMultipleObjects。那么你不需要任何计数器。当所有对象都发出信号(或超时)时,它会返回。
  • 您查看过 OmniThreadLibrary 吗?也许 ThreadCount 可能只是控制器的一个属性,而不是容易出错的手动计数变量

标签: multithreading delphi thread-safety


【解决方案1】:

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

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

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

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

并且变量不是增加两次,而是只增加一次。

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

【讨论】:

猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-04-03
  • 2014-02-01
相关资源
最近更新 更多