Multithreading Scala播放线程
我目前正在使用Scala/Play2 framework/MongoDB(reactivemongo) 我在请求中有这样一个函数:查找集合中的最大值,随机增加该最大值,将新值保存到该集合,然后返回新值Multithreading Scala播放线程,multithreading,mongodb,scala,future,Multithreading,Mongodb,Scala,Future,我目前正在使用Scala/Play2 framework/MongoDB(reactivemongo) 我在请求中有这样一个函数:查找集合中的最大值,随机增加该最大值,将新值保存到该集合,然后返回新值 def generateCode():Future[String] = { // find maximum maximum = Future[].... map { maxValue => // increase maxValue newValue = max
def generateCode():Future[String] = {
// find maximum
maximum = Future[].... map { maxValue =>
// increase maxValue
newValue = maxValue + random
// save back to database
}
}
问题是我希望这段代码一次只运行一个线程。因为若两个线程同时运行,那个么con值就会冲突。
例子:
线程1:读取最大值=100,线程2读取最大值=100
螺纹1:增加最大值=105,螺纹2增加最大值=102
线程1:将105保存到db,线程2将102保存到db
最后,db的最大值是102,实际上应该是105。
如何做到这一点?作为一项规则,ReactiveMongo API和未来的操作需要作用域中的隐式ExecutionContext。因此,您可以定义一个单线程执行上下文,并在定义
generateCode()
方法的类和调用ReactiveMongo API的类中使用它
import java.util.concurrent.Executors
implicit val ec: ExecutionContext = ExecutionContext.fromExecutor(Executors.newSingleThreadExecutor())
您还可以显式地将
ec
传递给需要隐式ExecutionContext的方法。您只需要确保整个异步方法调用链使用相同的单线程执行上下文。作为规则,ReactiveMongo API和未来的操作需要范围内的隐式ExecutionContext。因此,您可以定义一个单线程执行上下文,并在定义generateCode()
方法的类和调用ReactiveMongo API的类中使用它
import java.util.concurrent.Executors
implicit val ec: ExecutionContext = ExecutionContext.fromExecutor(Executors.newSingleThreadExecutor())
您还可以显式地将
ec
传递给需要隐式ExecutionContext的方法。您只需确保整个异步方法调用链使用相同的单线程执行上下文。您可以使用信号量
或ReentrantLock
来实现锁定:
val s = new ReentrantLock()
def generateCode():Future[String] = {
s.lock() //get lock block other threads to execute the db operation
// find maximum
maximum = Future[].... map { maxValue =>
// increase maxValue
newValue = maxValue + random
// save back to database
}
s.unlock()///after finish db operation, release this lock for other threads can get the Semaphore to continue work
}
您可以使用
信号量
或重入锁定
来实现锁定:
val s = new ReentrantLock()
def generateCode():Future[String] = {
s.lock() //get lock block other threads to execute the db operation
// find maximum
maximum = Future[].... map { maxValue =>
// increase maxValue
newValue = maxValue + random
// save back to database
}
s.unlock()///after finish db operation, release this lock for other threads can get the Semaphore to continue work
}
谢谢您的回答,但是查找最大值是未来的调用,所以我们应该将s.unlock()放在map函数中吗?是的,您可以尝试最小化锁定范围以增加cpu使用量我将s.unlock()放在回调函数中,并且它抛出非法的MonitorStateException锁应该在Future完成时释放,或者在对DB的写入完成后立即在Future内部释放
ReentrantLock
不允许在不同的线程中释放锁,这就是引发IllegalMonitorStateException
的原因<代码>信号量应该可以工作,谢谢你的回答,但找到最大值是未来的调用,所以我们应该将s.unlock()放在map函数中吗?是的,你可以尝试最小化锁定范围以增加cpu使用量我将s.unlock()放在回调函数中,并且它抛出非法的MonitorStateException锁应该在Future完成时释放,或者在对DB的写入完成后立即在Future内部释放ReentrantLock
不允许在不同的线程中释放锁,这就是引发IllegalMonitorStateException
的原因Semaphore
应该可以工作,尽管我使用了这种方法,而且当它在同一线程中访问generateCode方法时看起来很好。但是,如何使将来的内部调用也使用该线程呢?在所有期望单线程行为的地方使用相同的ExecutionContext隐式实例应该是可行的。为了明确起见,您可以将ec
直接传递给需要ExecutionContext作为隐式参数的方法。例如,未来[…]。。。。map{maxValue=>/*…*/}(ec)
我使用这种方法,当它在同一个线程中访问generateCode方法时,它看起来很好。但是,如何使将来的内部调用也使用该线程呢?在所有期望单线程行为的地方使用相同的ExecutionContext隐式实例应该是可行的。为了明确起见,您可以将ec
直接传递给需要ExecutionContext作为隐式参数的方法。例如,未来[…]。。。。映射{maxValue=>/*…*/}(ec)