Scala:如何测试 mutable.Set 的并发性
Scala: How to test the concurrency of a mutable.Set
在 Scala 中,并发和非并发 Set 的类型完全相同:
import collection.JavaConverters._
// A regular Set in Scala, not concurrent.
val regularSet: mutable.Set[Int] = mutable.Set[Int]()
// A concurrent set. It has the same type as a regular set, but underneath, it is actually concurrent. In my opinion, this is a flaw in the type system for Scala collections
val concurrentSet: mutable.Set[Int] = java.util.concurrent.ConcurrentHashMap.newKeySet[Int]().asScala
我想要一种实际测试集合是否并发的方法。
回答
您可以通过创建大量试图从共享 mutable.Set
add/remove 相同元素的线程来凭经验对其进行测试
import java.util.concurrent.{Callable, ExecutorService, Executors}
import scala.collection.mutable
def testSet(set: mutable.Set[Int]) {
val e: ExecutorService = Executors.newFixedThreadPool(5)
val tasks = for (i <- 0 to 50000) yield {
e.submit(new Callable[Unit]() {
override def call() {
for (j <- 0 to 10) {
set.add(i + j)
// This will throw a Null Pointer Exception for the non-concurrent version
// This is because it is trying to remove something that another thread already removed.
set.remove(i + j)
}
}
})
}
for (result <- tasks) result.get()
e.shutdown()
}
// Can't check the type! They have the same type.
val regularSet: mutable.Set[Int] = mutable.Set[Int]()
val concurrentSet: mutable.Set[Int] = java.util.concurrent.ConcurrentHashMap.newKeySet[Int]().asScala
testSet(concurrentSet) // Success!
testSet(regularSet) // FAILS! Almost always throws a NullPointerException on my system.
限制
运行 测试将需要系统资源,例如线程和 CPU 时间。在 运行 时间 运行 在生产中使用它并不合适。
这不是演绎证明。由于竞争条件是随机的,因此测试 class 将 non-concurrent 对象确认为并发的可能性很小。但是,运行延长测试时间会导致检测到 non-concurrent 对象的概率接近确定性。
附加评论
理想情况下,会有一种方法可以使用反射和类型系统来查看底层对象是什么,并测试它是否是 ConcurrentHashMap
(我认为 Scala 的主要缺陷,因为某些函数运行 一个 multi-threaded 任务不能有效地阻止函数调用者传入一个 non-concurrent 对象)。
但这至少提供了一种测试它的经验方法。
致谢
How can I test that ConcurrentHashMap is truly thread-safe? 中提出了类似的问题。我修改了它以使用 Sets:
推荐
我建议使用 concurrent.Map[T, Unit]
而不是 mutable.Set[T]
。原因是您将能够利用类型系统以 100% 的信心确保您的 function/class 操作的对象实际上是并发的。
是的,你会失去Set语义,比如.add(setMember)
方法。但你会获得安全感。
如果您坚持使用并发 mutable.Set
,请考虑制作一个包装器 class 容器,以防止意外初始化为 non-concurrent mutable.Set
。
在 Scala 中,并发和非并发 Set 的类型完全相同:
import collection.JavaConverters._
// A regular Set in Scala, not concurrent.
val regularSet: mutable.Set[Int] = mutable.Set[Int]()
// A concurrent set. It has the same type as a regular set, but underneath, it is actually concurrent. In my opinion, this is a flaw in the type system for Scala collections
val concurrentSet: mutable.Set[Int] = java.util.concurrent.ConcurrentHashMap.newKeySet[Int]().asScala
我想要一种实际测试集合是否并发的方法。
回答
您可以通过创建大量试图从共享 mutable.Set
import java.util.concurrent.{Callable, ExecutorService, Executors}
import scala.collection.mutable
def testSet(set: mutable.Set[Int]) {
val e: ExecutorService = Executors.newFixedThreadPool(5)
val tasks = for (i <- 0 to 50000) yield {
e.submit(new Callable[Unit]() {
override def call() {
for (j <- 0 to 10) {
set.add(i + j)
// This will throw a Null Pointer Exception for the non-concurrent version
// This is because it is trying to remove something that another thread already removed.
set.remove(i + j)
}
}
})
}
for (result <- tasks) result.get()
e.shutdown()
}
// Can't check the type! They have the same type.
val regularSet: mutable.Set[Int] = mutable.Set[Int]()
val concurrentSet: mutable.Set[Int] = java.util.concurrent.ConcurrentHashMap.newKeySet[Int]().asScala
testSet(concurrentSet) // Success!
testSet(regularSet) // FAILS! Almost always throws a NullPointerException on my system.
限制
运行 测试将需要系统资源,例如线程和 CPU 时间。在 运行 时间 运行 在生产中使用它并不合适。
这不是演绎证明。由于竞争条件是随机的,因此测试 class 将 non-concurrent 对象确认为并发的可能性很小。但是,运行延长测试时间会导致检测到 non-concurrent 对象的概率接近确定性。
附加评论
理想情况下,会有一种方法可以使用反射和类型系统来查看底层对象是什么,并测试它是否是 ConcurrentHashMap
(我认为 Scala 的主要缺陷,因为某些函数运行 一个 multi-threaded 任务不能有效地阻止函数调用者传入一个 non-concurrent 对象)。
但这至少提供了一种测试它的经验方法。
致谢
How can I test that ConcurrentHashMap is truly thread-safe? 中提出了类似的问题。我修改了它以使用 Sets:
推荐
我建议使用 concurrent.Map[T, Unit]
而不是 mutable.Set[T]
。原因是您将能够利用类型系统以 100% 的信心确保您的 function/class 操作的对象实际上是并发的。
是的,你会失去Set语义,比如.add(setMember)
方法。但你会获得安全感。
如果您坚持使用并发 mutable.Set
,请考虑制作一个包装器 class 容器,以防止意外初始化为 non-concurrent mutable.Set
。