Is it ok to have multiple threads writing the same values to the same variables?我了解竞争条件,以及多个线程如何访问同一个变量,一个人所做的更新可以被其他人忽略并覆盖,但是如果每个线程都向同一个变量写入相同的值(不是不同的值),那该怎么办? 甚至会引起问题吗? 此代码可以: GlobalVar.property = 11; (假设该属性永远不会被分配11以外的值),如果多个线程同时执行该属性会导致问题? 当您读回该状态并对其进行处理时,就会出现问题。编写是一条红鲱鱼-的确,只要这是一个单词,大多数环境就可以保证写操作是原子的,但这并不意味着包含该片段的较大代码段是线程安全的。首先,假设您的全局变量以不同的值开头-否则,如果您知道它始终相同,为什么它是变量?其次,想必您最终会再次读取此值? 问题是,大概是由于某种原因,您正在写入此共享状态位-表示发生了什么事吗?这就是它的缺点:当您没有锁定结构时,根本没有隐含的内存访问顺序。很难指出这里出了什么问题,因为您的示例实际上并不包含使用变量,因此,这是一个中立的类似于C的语法的琐碎示例:
线程A始终将打印1,但是对于线程B而言将打印0完全有效。仅要求线程A中的操作顺序可以从线程A中执行的代码中观察到-线程B可以查看状态的任何组合。对x和y的写入实际上可能不会按顺序进行。 即使在单处理器系统上也可能发生这种情况,在大多数人不期望这种重新排序的系统中,编译器可能会为您重新排序。在SMP上,即使编译器不对事物进行重新排序,内存写入也可能在独立处理器的缓存之间重新排序。 如果这似乎无法为您解决,请在问题中提供示例的更多详细信息。如果不使用该变量,就不可能确切地说出这种用法是否安全。 这取决于该语句实际完成的工作。在某些情况下,还是会发生不良情况-例如,如果C ++类重载了=运算符,并且在该语句中执行了一些重要的操作。 我不小心编写了使用POD类型(内置原始类型)执行类似操作的代码,并且效果很好-但是,这绝对不是一个好习惯,并且我不确定它的可靠性。
为什么不在使用该变量时将其锁定在该变量周围?实际上,如果您以某种方式"知道"这是可能在代码中的某个时刻出现的唯一写语句,为什么不直接使用值11而不是将其写入共享变量呢? 如果您要使用它来确定何时至少有一个线程到达此语句,则可以使用从1开始的信号量,并由第一个命中该信号的线程减少。 这是我对这个问题的看法。 您有两个或多个正在运行的线程正在写一个变量,例如状态标志之类的东西,您只想知道其中一个或多个是否为真。然后,在代码的另一部分(线程完成之后),您要检查并查看是否至少在线程上设置了该状态...例如
我相信上面的代码是可以的,假设您不知道哪个线程将状态设置为true就可以了,并且您可以等待所有多线程操作完成后再读取该标志。我可能是错的。
通常,除非您的系统提供了原子操作(保证在单个周期内执行的操作),否则这不是安全的事情。 根据您的操作系统,您可以执行以下操作:
我希望结果不因为它会因编译器而异,语言会因语言而异,而OS会因OS而异。因此,不安全 但是,为什么要执行此操作-在一行中添加一条以获得互斥锁的功能只是一两行代码(在大多数语言中),这样就可以消除出现问题的可能性。如果这将是两次昂贵,那么您需要找到解决问题的另一种方法 假设该属性永远不会被分配给11以外的任何东西,那么我首先看不到有归属的原因。只需使其恒定即可。 只有在您打算更改该值时,才有意义分配,除非分配行为本身具有其他副作用-就像volatile写操作在Java中具有内存可见性的副作用一样。而且,如果您更改了多个线程之间共享的状态,则需要同步或"处理"并发问题。 如果在没有适当同步的情况下将值分配给多个线程之间共享的某些状态,则无法保证其他线程何时会看到该更改。没有可见性保证意味着其他线程可能永远看不到分配。 编译器,JIT,CPU缓存。他们都在试图使您的代码尽可能快地运行,并且如果您对内存可见性没有任何明确的要求,那么他们将利用这一优势。如果不在您的计算机上,则其他人。 如果该操作是原子操作,则应该可以顺利进行。但是我不会在实践中这样做。最好仅获得对象的锁并写入值。 |