在多线程环境中编写和读取字符串

时间:2014-10-15 14:53:26

标签: string multithreading delphi

同时运行两个线程在同时写入和读取两个线程的变量时会产生奇怪的行为。它可以是线程安全的,但不是在所有情况下。

线程安全示例:TThread.Terminated

布尔终止只读FTerminated,它只设置一次,因为它是布尔值,所以写入过程是原子的。因此,可以在MainThread和线程中读取该值,并且始终可以读取线程安全。

我的例子:我有一个字符串,只写一次。与TThread.Terminated不同,我的字符串的写入不是原子的,因此读取它本身并不是线程安全的。但是在特殊情况下可能存在线程安全的方式:我有一种情况,我只想将字符串与另一个字符串进行比较。如果它们是相同的,我只会做一些事情(如果它们不相等则并不重要,因为字符串尚未完全写入)。所以我想到这是否可以是线程安全的。那么当字符串被写入时会发生什么呢?如果我只读了一半就读了字符串会出错?

编写字符串时要执行的步骤:

  1. 参考计数= 1:
    1. 如果新字符串比旧字符串长
    2. ,则分配额外内存
    3. 复制字符
    4. 设置新字符串长度
    5. 如果新字符串比旧字符串短,则取消分配内存
  2. 参考计数> 1(由于写时复制语义,需要一个新的字符串实例):
    1. 为新字符串实例分配内存
    2. 将字符复制到新位置并设置字符串的长度
    3. 找到指向新位置的字符串实例指针
  3. 在什么情况下读取在同一时刻写入的字符串是否安全?

    1. 参考计数= 1:
      1. 如果步骤的顺序如上所列,并且在设置其长度之前读取字符串仅返回设置的长度(不是所有分配的字节),那么它只是(并且在这种情况下总是)是安全的。
    2. 参考计数> 1:
      1. 如果将指向字符串的指针设置为最后一步(因为设置此指针是原子操作)或者在指向指针之前将长度初始化为0,则只读(并且在这种情况下始终)是安全的。设置了字符串,并且案例“引用计数= 1”的条件适用于新字符串
    3. 对那些具有如此深刻知识的人提出质疑:我的假设是真的吗?如果是的话,我可以安全地依赖它吗?或者依靠这个实现细节是一个如此糟糕的想法,它甚至不值得考虑所有这些并且只是在它们被写入另一个线程时不会无保护地读取字符串?

2 个答案:

答案 0 :(得分:3)

Delphi字符串是“线程安全的”,只是在某种意义上,字符串的引用计数在多线程代码中保证有效。

Delphi字符串的Copy-On-Write是线程安全操作;如果您需要对同一个字符串进行多线程读/写访问,通常应该使用一些同步,否则您可能会遇到麻烦。

答案 1 :(得分:0)

在没有任何锁的情况下可能发生的情况的示例。

正在写入字符串:它应该变得比以前更大,因此将分配新的内存。但是指针尚未修改,它指向旧字符串。

同时,读取线程获得了一个指针,并开始读取旧字符串。

上下文再次切换到写入线程。它更改了指针,因此现在有效。旧字符串的引用计数为0,并立即被释放。

再次进行上下文切换:读取线程继续处理旧字符串,但是现在可以访问释放的内存,这很容易导致访问冲突。