TThread.Synchronize是邪恶的/夭折了吗?

3
我得到的印象是一个至少有一点可控性的线程在主线程上必须完全不使用 Synchronize() :任何尝试这样做几乎立即导致死锁在一个情况或另一个情况下。
因此,也应该避免使用 onTerminate 事件,因为它将使用 Synchronize 被调用。
例如,我有一个执行某些繁重后台工作的线程。我想在状态栏中观察其进度,并在用户按下关闭按钮时能够“优雅地”停止它。
我不能使用 FreeOnTerminate := true,因为一旦线程启动,我就不能调用其任何方法:在任何时刻,它可能会被销毁,并且我会获取访问冲突(或完全损坏)。
因此,线程的销毁是主线程的责任。在某种完成工作的 DoTerminate 过程中执行所有“完成”工作并将 onTerminate 事件指向它将是合理的。但是这容易造成死锁:我们无法从 DoTerminate 中释放线程,因为 TThread.Free 中有 WaitFor ,它不能在同步 onTerminate 事件完成之前完成。
不仅如此,如果工作线程在其中有任何 Synchronize 调用(例如,它想通知我们已经完成了一些工作百分比等),则在这个非常时刻如果主线程正在进行某些操作,它们会相互阻塞!
因此,在我看来,使用 Synchronize 的唯一方式是将所有调用委托给工作线程端!例如,我们将其设置为 FreeOnTerminate := true 并且它有时会使用 Synchronize() 告诉我们它的进度,或者已经完成并将被销毁。仅在这些过程中,我们才能控制它,但它使优雅地随意关闭几乎不可能或过于复杂。
我是否遗漏了什么(一些内部机制可以以某种方式克服这些死锁)?因为我有点惊讶:Synchronize() 是 Delphi 多线程手册中描述的第一个方法之一。它真的那么无用吗?

1
你能发一下你的代码吗?我在这里试过了,它运行得很好。 - Kohull
1
为什么这个问题被踩了?我完全理解了没有示例代码的观点。 - MichaSchumann
4
同步本身并不是问题,程序架构才是关键。例如,在持有关键部分时调用Synchronize就会有问题(这个做法可能没问题,但更容易导致灾难性后果)。你对同步的问题在于如何使线程之间进行通信。 - Ken Bourassa
1个回答

9

Synchronize的用途完全合理,但是您需要仔细分析这些用例,并确保编写代码时明确避免死锁情况。这是完全可能的,但需要仔细设计。

通常只有在需要工作线程等待主线程完成工作后才能继续工作时,才应该使用Synchronize。在大多数情况下,这是不必要的,通常最好使用TThread.Queue代替。使用Queue将工作提交给主线程,然后立即返回而无需等待主线程处理工作。这避免了大多数围绕Synchronize使用的死锁陷阱。

Queue的唯一危险是使主线程超载。如果您的工作者比主线程更快地将工作提交到主线程,则可能会导致主线程锁定以处理排队的工作。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接