【问题标题】:Can we prevent shared mutable state if we only allow local variables to be mutable?如果我们只允许局部变量是可变的,我们可以防止共享可变状态吗?
【发布时间】:2017-11-14 06:48:36
【问题描述】:

在非 OOP 编程语言中,例如 C,如果我们只允许局部变量以各种可能的方式进行变异(更改内部字段、重新分配,...)但不允许函数参数的变异,会有帮助吗?我们阻止共享可变状态?

请注意,在这种情况下,函数 main 可以启动 10 个线程(函数),并且这 10 个线程中的每一个都将接收到对同一变量(在 main 中定义)的不可变引用。但是main 函数仍然可以更改该共享变量的值。那么这会导致并发/并行软件出现问题吗?

我希望问题很清楚,但如果不是,请告诉我。

附: “软件事务内存(STM)”能否解决潜在问题?喜欢 Clojure 提供的功能吗?

【问题讨论】:

  • main 与任何其他线程没有什么不同。因此,在所描述的情况下,如果单个线程更改共享变量,您将遇到完全相同的多线程问题。
  • @4386427 注意只允许一个线程写入内存的假设。所有其他线程只读取...仍然存在问题(我正在写一个答案),但人们通常认为这会有所帮助。
  • 如果main() 修改了变量但线程应该从中读取不同的值,那么您还没有解决问题。这可能是 POSIX pthread 的问题。 pthread_create() 函数接受 void * 最后一个参数,一个常见的错误是传递循环控制变量的地址——这意味着线程无法知道它们将要读取什么(值可能在函数被调用并启动线程)。
  • 这并不能解决任何问题
  • @mahdix ,感谢您说清楚。我认为这不能为代码提供足够的保护,即使与 volatile 关键字结合使用,正如我的回答中所解释的那样。

标签: c multithreading concurrency clojure immutability


【解决方案1】:

是和否...这取决于平台、CPU、共享变量的大小和编译器。

NVIDIA forum上,关于GPU操作,一个类似的问题得到了非常巧妙的回答:

当多个线程在全局内存中对自然对齐的位置进行写入或读取时,并且所有线程正在读取或写入的数据类型相同,并且数据类型对应于单指令线程支持的类型之一访问...

(很多 GPU 单指令在事先知道的情况下可以处理 16 字节字(128 位),但大多数 CPU 使用单指令 32 位或 64 位限制)

我不考虑线程可能从 CPU 寄存器而不是实际内存读取的机会(忽略对数据的更新),这些大多可以使用 C 中的 volatile 关键字来解决。

但是,冲突和内存损坏仍然可能发生。

一些内存存储操作在内部(由 CPU)或由您的编译器(机器代码)使用大量存储调用处理。

在这些情况下,主要是在多核机器上(但不仅如此),“读者”可能会收到部分更新且没有任何意义的信息(即,指针的一半是有效的,而其他不是)。

大于 32 位或 64 位的变量通常会一次更新一个 CPU“字”(不是操作系统字)(32 位或 64 位)。

字节大小的变量非常安全,这就是为什么它们经常被用作标志......但它们可能应该使用操作系统或编译器提供的atomic_* 存储/写入操作来处理。

【讨论】:

  • “软件事务内存(STM)”能否解决潜在问题?喜欢 Clojure 提供的功能吗?
  • @mahdix - STM 可能会保护您(因为日志和数据不适合,因此将执行回滚)...但这通常是一种语言功能。它可以在 C 中实现(可能是),但它只是使用锁的替代方案......恕我直言,甚至不是一个很好的替代方案。 C 不会自动化 STM 的使用(如果你问我,这有点超出了它的目的),所以你必须用 STM 逻辑来包围每个读写操作。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-08-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多