Racy尾递归函数

时间:2013-12-18 17:47:51

标签: scala concurrency buffer race-condition

我正在尝试使用尾递归函数对SynchronizedQueue进行一些处理。该函数似乎工作正常,但我对并发性的考虑越多,我相信在使用不同的线程访问此队列时,我可能会遇到一些竞争条件。这是我认为我可以使用的功能:

val unsavedMessages = new SynchronizedQueue[CachedMessage]()

val MAX_BATCH = 256
val rowCount = new AtomicInteger()

  private def validateCacheSize() = if (unsavedMessages.length > MAX_BATCH) {
    implicit val batch = createBatch
    val counter = rowCount.getAndIncrement
    @tailrec
    def processQueue(queue: SynchronizedQueue[CachedMessage]): Unit = if (queue.nonEmpty) {
      val cm = queue.dequeue
      addToBatch(cm.request, cm.timestamp, cm.brokerId, counter)
      processQueue(queue)
    }
    processQueue(unsavedMessages)
    executeBatch
    resetQueue
  }

  def resetQueue = unsavedMessages.clear

多个线程调用此函数:

  def add(request: WebserviceRuleMatch, timestamp: Long, brokerId: String) = {
    validateCacheSize
    //log.info("enquing request "+ unsavedMessages.length)
    unsavedMessages.enqueue(CachedMessage(request, timestamp, brokerId))
  }

有没有人对如何改善这一点有任何指示,以便可能没有竞争条件?

2 个答案:

答案 0 :(得分:1)

  

从未来调用add函数,所以我觉得队列可能在queue.nonempty和queue.dequeue之间被清空。

是的,它可以。您可以使用双重检查锁定来使validateCacheSize单线程。 (顺便说一下,这种方法似乎有一个非常误导的名字!)

  

另外我认为可以通过processQueue和resetQueue之间的线程添加消息。

是的,他们可以。但为什么你需要打电话给unsavedMessages.clearqueue.dequeue已将其从队列中删除。因此,队列中应该存在的唯一unsavedMessages是仍然需要处理的。{/ p>

答案 1 :(得分:1)

  

队列有可能在queue.nonempty和queue.dequeue之间清空队列

  • 避免调用必须在代码中同步的多个队列操作。使用SynchronizedQueue的强大功能进行原子线程安全操作。例如。避免完全调用queue.nonempty(替代尾递归):

    for (cm <- unsavedMessages.dequeueAll(_ => true)) 
      addToBatch(cm.request, cm.timestamp, cm.brokerId, counter)
    executeBatch
    //resetQueue   -- Don't do this!  Not thread-safe
    
  

我认为可以通过processQueue和resetQueue

之间的线程添加消息
  • 总会有一个点,您的代码已经对队列进行了“快照”并将其清空。我之前的观点确保'快照'和清空是一个单一的原子操作。如果新条目在该原子'快照&amp;之后的任何点排队。空'操作 - 没问题。你的快照&amp;空的'必须在某个地方出现,新的物品入队是生活中的事实。决定允许新项目在“快照”和“快照”之后的任何时间点排队。空”。它们将在下一个周期进行处理。也就是说,除了上述点之外没什么需要的。
  罗宾格林:(顺便说一句,这种方法似乎有一个非常误导的名字!)

  • 他说了! :)