【问题标题】:Is this a bug of TThread under android?这是android下TThread的bug吗?
【发布时间】:2016-11-17 22:11:37
【问题描述】:

在 windows 上,我们可以在同一个线程上多次调用 MyThread.waitfor。如果线程已经终止没问题,这不会引发任何异常并立即返回(正常行为)。

在 Android 上,情况有所不同,如果我们调用两次 MyThread.waitfor,那么我们将在第二次尝试时出现“No such process”异常。

function TThread.WaitFor: LongWord;
{$ELSEIF Defined(POSIX)}
var
  X: Pointer;
  ID: pthread_t;
begin
  if FExternalThread then
    raise EThread.CreateRes(@SThreadExternalWait);
  ID := pthread_t(FThreadID);
  if CurrentThread.ThreadID = MainThreadID then
    while not FFinished do
      CheckSynchronize(1000);
  FThreadID := 0;
  X := @Result;
  CheckThreadError(pthread_join(ID, X));
end;
{$ENDIF POSIX}

错误是因为在调用时等待他们设置 FThreadID := 0 所以当然任何进一步的调用都会失败

我认为它必须写成:

function TThread.WaitFor: LongWord;
{$ELSEIF Defined(POSIX)}
begin
  if FThreadID = 0 then exit;
  ...
end;
{$ENDIF POSIX}

你觉得呢?我需要在 emb 提出错误请求吗?

【问题讨论】:

    标签: android delphi firemonkey


    【解决方案1】:

    pthread_join 的文档说:

    加入之前已加入的线程会导致未定义的行为。

    这解释了为什么TThread 会采取措施避免调用未定义的行为。

    设计有缺陷吗?这是值得商榷的。如果我们要考虑这个类的设计,让我们扩大讨论,因为设计师必须这样做。一个 Windows 线程可以被多个不同的线程等待。这不是 pthreads 的情况。链接的文档还说:

    如果多个线程同时尝试加入同一个线程,结果是不确定的。

    所以我认为 Embarcadero 无法在 Posix 平台上合理地实现与 Windows 上已经存在的相同行为。正如您所描述的,他们肯定可以特殊情况下来自同一线程的重复等待。好吧,他们必须保留线程返回值,以便WaitFor 可以返回它。但这只会让你走上一段路,而且无论如何也不会很有用。毕竟,你为什么要从同一个线程再次等待?

    我怀疑 FThreadID 设置为 0 是为了避免未定义的行为并以更稳健的方式失败。但是,如果多个线程调用 WaitFor 则存在数据竞争,因此仍可能出现未定义的行为。

    如果我们想做慈善,那么我们可以

    将这些具体细节留到一边,很明显如果WaitFor 是通过调用pthread_join 实现的,那么跨平台的不同行为是不可避免的。 Embarcadero 已尝试为每个平台调整 TThread 实现,但由于平台功能不同,它们不能完全等效。 Windows 提供了比 pthreads 更丰富的线程原语集。

    如果 Embarcadero 选择了不同的路径,他们可以完美地调整平台,但需要在 Posix 上更加努力地工作。可以在那里复制 Windows 的行为,但是这个特定的方法必须用 pthread_join 以外的东西来实现。

    不过,面对现实,我认为您将不得不适应 pthread 的不同功能。在 pthreads 中,等待线程的能力只是为了方便。如果您确实想要支持重复等待,那么您最好等待事件或条件变量。另一方面,您可能只是重新创建代码以确保您只等待一次。

    因此,总而言之,您可能应该向 Embarcadero 提出问题,如果还没有问题的话。他们可能会考虑支持您的方案。值得在系统中遇到问题。但是,如果他们选择不做任何事情并证明这是因为无法克服的更广泛的平台差异以及类中需要额外的复杂性来支持您有些毫无意义的用例,请不要感到惊讶。我希望我们都能同意的一件事是TThread.WaitFor 的 Delphi 文档应该涵盖这些问题。

    【讨论】:

    • 感谢 david,但是如果您查看 waitfor 的实现,他们会设置 FThreadID := 0 因此任何进一步调用 waitfor 都可以检查 FThreadID = 0 是否立即返回?
    • 我想他们可以做到。但是他们不能让多个线程等待。你为什么要从同一个线程等待两次。
    • 这实际上不是关于多线程,而是关于多个函数 .. 例如,当您有多个函数(全部从主线程调用)需要等待线程完成才能继续(这些功能都是独立的)。所以所有然后做一个相当测试MyThread.waitfor。当然我可以通过如果不是 MyThread.checkTerminated 然后 MyThread.waitfor 来更新所有功能
    • 在这种情况下,不要等待线程本身。创建一个单独的TEvent 并等待它,并让线程在完成时发出事件信号。如果需要,这还允许您在将来迁移代码以使用线程池,而无需再次更改等待代码。
    • “未定义”表示 未定义。即由于 FireMonkey 应该提供跨不同平台的抽象,在这些平台上可以实现一致的行为并且不会与 已定义 行为,则没有理由保持一致。在这种情况下,线程 A 在 B 上的连续等待可能是一致的。在 multiple 同一线程上的线程不能保持一致(但仍然未定义,因此可以以任何你喜欢的方式不一致)。它也不会改变这样一个事实,即可能有更好的方法来做 OP在之后(根据雷米的评论)。
    猜你喜欢
    • 1970-01-01
    • 2017-03-05
    • 2012-12-21
    • 2013-04-12
    • 2011-09-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多