在Scala - 关于coursera的函数式编程课程中,以下片段在并发案例中被描述为失败。如果mutable.Set
被并发类替换,则代码段将成功。
def intersection(a: GenSet[Int], b: GenSet[Int]): Set[Int] = {
val result = mutable.Set[Int]()
for (x <- a) if (b contains x) result += x
result
}
intersection((0 until 1000).toSet, (0 until 1000 by 4).toSet)
intersection((0 until 1000).par.toSet, (0 until 1000 by 4).par.toSet)
规则:避免在没有适当的情况下突然转移到相同的内存位置 同步。
并发和非并发类之间的区别是什么,或者并发类在并行化下可能失败的原因是什么?
答案 0 :(得分:2)
如果同时将元素附加到设置而没有同步,则设置无法正确处理冲突或错误地计算位置。因此,对于您的示例,res2
可能会有重复字段或更少某些字段。
移出:
有:
for (x <- a) if (b contains x) result += x
result
}
result += x
存在竞争条件。它等于,但对于这种方法,它不是线程安全,
var h = index(newEntry.hashCode)
var curEntry = table(h)
while (null != curEntry) {
if (curEntry == newEntry) return false
h = (h + 1) % table.length
curEntry = table(h)
//Statistics.collisions += 1
}
table(h) = newEntry
在上面的代码中,尝试同时将元素追加到 HashTable 。它可能导致计算错误的位置或遇到错误的碰撞。例如,当尝试将newEntry
添加到Set
时,实际上它不存在于集中,它将直接转到table(h) = newEntry
,但同时时间,有一个新值,它与hashcode
具有相同的newEntry
,但是对于第一个newEntry
仍未完成table(h) = newEntry
,所以newEntry
将被第二值覆盖。
因此对于同步,您可以这样做:
for (x <- a) {
if (b contains x) {
this.synchronized {
result += x
}
}
}
答案 1 :(得分:1)
如果您的代码包含可变字段,那么它不是线程安全的。这意味着,您无法同时执行此代码 。并发执行意味着,同时在多个线程中执行相同的代码。
因此,让我们从您的给定代码开始,因为它包含一个可变字段result
,并且此字段在for
循环内被修改/变异。
def intersection(a: GenSet[Int], b: GenSet[Int]): Set[Int] = {
val result = mutable.Set[Int]() //This field is not thread safe.
for (x <- a) if (b contains x) result += x //mutation occured here.
result
}
现在,让我们看看第一种情况的执行情况:intersection((0 until 1000).toSet, (0 until 1000 by 4).toSet)
。在这里,您提供了值为0 to 1000
的Set。现在,在for loop
中,当迭代此集合时,执行将是顺序的。即x
的值从0 till 1000
顺序写入结果。因此,代码执行时没有任何错误。
现在,让我们看看第二个方案intersection((0 until 1000).par.toSet, (0 until 1000 by 4).par.toSet)
。在这种情况下,您提供的集合为Parallel set
。因此,在for loop
中,突变/写入结果字段同时发生。因此,线程之间将存在竞争条件以便写入数据,因此该执行不是线程安全的并且将失败。要避免此问题,必须按照其他答案中的描述同步对结果的写访问权限,否则,它将不是线程安全的。
因此,有一个并发规则:
&#34;避免在没有适当的情况下突然转移到相同的内存位置 同步&#34;