【问题标题】:multi-threading thread safety多线程线程安全
【发布时间】:2022-11-21 23:30:45
【问题描述】:

我正在使用 Delphi TThread 运行到外部设备的多个 TCP 连接。传入的数据被分解和存储。一切正常,但在审查我的代码时,我怀疑它是否是线程安全的,或者到目前为止我是否幸运......

TThread.Execute 方法中,我调用了一个存在于不同单元中的辅助函数,而不是任何 TThread 或其他类的成员。该函数访问缓冲区中的四个字节数据,反转字节顺序并将结果放入Single 类型的变量中(外部设备为大端)。

type
  TByteBuffer = array [0 .. 255] of Byte;

function ConvBufferToSingle(Buffer: TByteBuffer; J: Byte): Single;
type
  TSingleByteArray = array [0 .. 3] of Byte;
var
  X: Single;
begin
  TSingleByteArray(X)[3] := Buffer[J];
  TSingleByteArray(X)[2] := Buffer[J + 1];
  TSingleByteArray(X)[1] := Buffer[J + 2];
  TSingleByteArray(X)[0] := Buffer[J + 3];
  Result := X;
end;

虽然这似乎可行,但我无法理解如果一个(或多个)线程在另一个线程退出之前进入此函数会发生什么。我读过关于线程安全对比重新进入代码,但我仍然不确定如何验证代码是线程安全的。

当函数被调用时,我相信参数被压入堆栈。对于每个 TThread 进入函数都会发生这种情况,但是解决这些变量的代码中发生的事情让我感到困惑。

为了省心,把函数变成线程类的方法是不是更好的做法?

【问题讨论】:

  • 您展示的函数是完全线程安全的,只要每个线程都使用自己的 TByteBuffer 调用它,而不是与其他线程共享(或者,如果共享,则对它的访问已在线程之间充分同步) .
  • 方法只是具有额外隐藏参数的函数。方法中没有什么比函数更线程安全或不安全的。这完全取决于函数或方法的编写方式以及它访问的共享数据类型(如果有)。如果函数或方法不直接访问任何共享数据,就像你的不一样,那么它本身就是线程安全的。但是,如果您作为参数传递的数据在多个线程之间共享,那么代码的线程安全性将取决于更广泛的上下文,而不仅仅是函数本身,就像 Remy 已经提到的那样。
  • 有点不清楚你的意思是“每个进入函数的 TThread 都会发生这种情况,但是处理这些变量的代码中发生的事情让我感到困惑。”您在这里谈论的是哪些变量、哪些代码以及在什么时间?

标签: multithreading delphi thread-safety tthread


【解决方案1】:

该代码不是线程安全的。 TSingleByteArray 是托管类型。从多个线程同时读取或写入托管类型是不安全的。 最简单的解决方案是使该函数成为线程类的方法。这样每个线程都有自己的函数实例,因此也有自己的TSingleByteArray。 或者,您可以使用 TInterlocked 函数使代码线程安全。例如:

type
  TSingleByteArray = array [0 .. 3] of Byte;
var
  X: Single;
begin
  TInterlocked.Exchange(TSingleByteArray(X)[3], Buffer[J]);
  TInterlocked.Exchange(TSingleByteArray(X)[2], Buffer[J + 1]);
  TInterlocked.Exchange(TSingleByteArray(X)[1], Buffer[J + 2]);
  TInterlocked.Exchange(TSingleByteArray(X)[0], Buffer[J + 3]);
  Result := X;
end;
</code>

【讨论】:

    猜你喜欢
    • 2012-03-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-07-05
    • 2012-11-18
    相关资源
    最近更新 更多