我理解竞争条件以及多个线程如何访问同一个变量,一个更新所做的更新可以忽略并被其他人覆盖,但是如果每个线程都在向同一个变量写入相同的值(不是不同的值),那该怎么办呢?甚至可能导致问题?可以这段代码:
GlobalVar.property = 11;
(假设该属性永远不会被分配给11以外的任何东西),如果多个线程同时执行它会导致问题吗?
答案 0 :(得分:8)
问题来自于您回读该状态并对其做些什么。写作是一个红色的鲱鱼 - 确实,只要这是一个单词,大多数环境保证写入将是原子的,但这并不意味着包含此片段的更大代码片段是线程安全的。首先,大概你的全局变量包含一个不同的值 - 否则如果你知道它始终是相同的,为什么它是变量?其次,大概你最终会再次读取这个值吗?
问题是,大概是因为某种原因你正在写这个共享状态 - 发出信号已经发生了什么?这就是它失败的地方:当你没有锁定结构时,根本没有隐含的内存访问顺序。很难指出这里有什么问题,因为你的例子实际上并不包含变量的 use ,所以这里是中性C语法的一个简单例子:
int x = 0, y = 0;
//thread A does:
x = 1;
y = 2;
if (y == 2)
print(x);
//thread B does, at the same time:
if (y == 2)
print(x);
线程A将始终打印1,但它对于线程B打印0完全有效。线程A中的操作顺序只需要从线程A中执行的代码中观察到 - 线程B被允许看到任何组合国家。对x和y的写入可能实际上不是按顺序发生的。
即使在单处理器系统上也会发生这种情况,大多数人都不希望这种重新排序 - 您的编译器可能会为您重新排序。在SMP上,即使编译器没有重新排序,也可以在单独处理器的高速缓存之间重新排序内存写入。
如果这似乎没有为您解答,请在问题中包含您的示例的更多详细信息。如果不使用变量,就不可能明确地说明这种用法是否安全。
答案 1 :(得分:2)
这取决于该声明实际完成的工作。在某些情况下仍然会发生一些错误 - 例如,如果C ++类重载了=运算符,并且在该语句中做了一些非常重要的事情。
我不小心编写了使用POD类型(内置原始类型)执行此类操作的代码,并且工作正常 - 但是,这绝对不是一种好的做法,而且我不相信它是可靠的。
为什么不在使用它时锁定此变量周围的内存?实际上,如果你以某种方式“知道”这是代码中某个点可能出现的唯一写语句,为什么不直接使用值11,而不是将其写入共享变量? (编辑:我想在代码中直接使用常量名称而不是magic number 11更好。顺便说一句。)
如果您正在使用它来确定何时至少有一个线程到达此语句,您可以使用从1开始的信号量,并通过命中它的第一个线程递减。
答案 2 :(得分:1)
我希望结果不确定。因为它会从编译器到编译器,语言和操作系统到操作系统等不同。所以不,它不安全
你想要做什么呢 - 添加一行来获取互斥锁只是一行或两行代码(在大多数语言中),并且会消除任何问题的可能性。如果这将是两个昂贵的,那么你需要找到解决问题的另一种方法
答案 3 :(得分:1)
一般情况下,除非您的系统提供原子操作(保证在一个周期内执行的操作),否则这不被认为是安全的事情。 原因是虽然“C”语句看起来很简单,但通常会发生许多基础组装操作。
根据您的操作系统,您可以执行以下操作:
答案 4 :(得分:1)
这是我对这个问题的看法。
你有两个或多个运行的线程写入变量...就像状态标志或其他东西,你只想知道它们中的一个或多个是否为真。然后在代码的另一部分(在线程完成之后),您要检查并查看是否至少在线程上设置了该状态...例如
bool flag = false
threadContainer tc
threadInputs inputs
check(input)
{
...do stuff to input
if(success)
flag = true
}
start multiple threads
foreach(i in inputs)
t = startthread(check, i)
tc.add(t) // Keep track of all the threads started
foreach(t in tc)
t.join( ) // Wait until each thread is done
if(flag)
print "One of the threads were successful"
else
print "None of the threads were successful"
我相信上面的代码没问题,假设您不知道哪个线程将状态设置为true,并且您可以在读取该标志之前等待所有多线程内容完成。我可能错了。
答案 5 :(得分:0)
假设财产永远不会被分配给11以外的任何东西,那么我首先看不出分配的理由。那就让它变成一个常数。
当您打算更改值时,Assigment才有意义,除非分配行为本身具有其他副作用 - 例如易失性写入在Java中具有内存可见性副作用。如果你改变多个线程之间共享的状态,那么你需要同步或“处理”并发问题。
如果在没有正确同步的情况下为多个线程之间共享的某个状态分配值,则无法保证其他线程何时会看到该更改。并且没有可见性保证意味着其他线程可能永远不会看到该受让人。
编译器,JIT,CPU缓存。他们都试图让你的代码尽可能快地运行,如果你没有对内存可见性做出任何明确的要求,那么他们就会利用这一点。如果没有在你的机器上,那么有人就可以了。
答案 6 :(得分:-1)
如果操作是原子操作,那么应该能够正常运行。但我不会在实践中这样做。最好只获取对象的锁并写入值。