【问题标题】:writing and reading strings in multithread environment在多线程环境中读写字符串
【发布时间】:2014-10-15 14:53:26
【问题描述】:

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

线程安全示例:TThread.Terminated

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

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

写字符串的步骤:

  1. 引用计数 = 1:
    1. 如果新字符串比旧字符串长,则分配额外的内存
    2. 复制字符
    3. 设置新的字符串长度
    4. 如果新字符串比旧字符串短,则释放内存
  2. 引用计数 > 1(由于写时复制语义,需要一个新的字符串实例):
    1. 为新的字符串实例分配内存
    2. 将字符复制到新位置并设置字符串长度
    3. 将字符串实例指针定位到新位置

在什么情况下读取在同一时刻写入的字符串是安全的?

  1. 引用计数 = 1:
    1. 只有(并且在这种情况下总是)安全读取如果步骤顺序如上所列,并且在设置长度之前读取字符串只会返回设置长度(不是所有分配的字节)李>
  2. 引用计数 > 1:
    1. 只有在将指向字符串的指针设置为最后一步(因为设置此指针是原子操作)或在指向字符串已设置,“参考计数 = 1”的情况适用于新字符串

向拥有如此深厚知识的人提问:我的假设是否正确?如果是,我可以安全地依赖它吗?或者依赖这个实现细节是一个糟糕的主意,甚至不值得考虑所有这些,只是在将字符串写入另一个线程时不要在不受保护的情况下读取它们?

【问题讨论】:

  • 对于Delphi字符串,如果你有一个线程写入和一个线程读取,它永远不会是线程安全的。用锁保护。我的答案中的TThreadsafe<T> 类可以完成这项工作:stackoverflow.com/questions/19703274/…
  • @David 当然,这将是一种简单的方法,但我想更深入地研究它。如果我不需要,为什么要同步到线程?你能解释一下为什么在我概述的情况下它不是线程安全的吗?
  • 如果你不需要同步,那么肯定不要。我不认为这是容易或困难之间的选择。我并不是建议您选择简单的方法。我认为正确性应该是这里的目标。你不同意吗?
  • 由于您只写入一次字符串,因此您可以通过添加额外的布尔值来避免锁定。写入字符串,并将布尔值设置为 true。如果要读取字符串,请在尝试读取字符串之前检查布尔值。这有效,但增加了复杂性。没有任何好处。像临界区这样的锁只有在发生争用时才会昂贵。由于您只写一次,因此实际上不会发生争用。
  • @David 可能会发生争用,因为我多次尝试读取这个字符串,直到它相等。这可以满足于编写它的那一刻。无论如何,这可以称为微优化,我只是为了学习它而深入研究它。总之,您是说在没有任何保护措施的情况下读取该字符串可能会导致读取未初始化的数据甚至访问冲突?

标签: string multithreading delphi


【解决方案1】:

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

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

【讨论】:

    【解决方案2】:

    没有任何锁定可能发生的示例。

    正在写入字符串:它应该变得比原来更大,因此分配了新的内存。但是指针还没有被修改,它指向的是旧字符串。

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

    上下文再次切换到写入线程。它改变了指针,所以现在它是有效的。旧字符串得到 refcount 0 并立即被释放。

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-11-23
      • 2018-08-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多