Multithreading Scala播放线程

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

我目前正在使用Scala/Play2 framework/MongoDB(reactivemongo) 我在请求中有这样一个函数:查找集合中的最大值,随机增加该最大值,将新值保存到该集合,然后返回新值

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)