サイエンスデザインノート

仕事やプログラミングやそうでないことwo覚え書くブログです

Scala で ReadWriteLock にローンパターンを使う

ReadWriteLock は確実にロックを解除しないといけません。

  readWriteLock.writeLock().lock();
  try {
    // 何かの処理
  }
  finally {
    readWriteLock.writeLock().unlock();
  }

いけませんが、よく忘れます。unlock() がコピペの魔術かなにかで lock() になったままとか。writeLock() をロックして readLock() をロック解除とか。

try-finally 句を使ってロックは確実にロック解除させるべきですが、ロックをかけるだけでコードが助長になります。

確実にリソースを閉じるときなど、ローンパターンを用いた using (with) 句がとても便利です。ということで、Scala で ReadWriteLock をするとき(あまり無いんじゃないかという話もありますが)、ローンパターンでロックを管理させるための、構文です。

object LoanPatternLock {
  import java.util.concurrent.locks.{Lock, ReadWriteLock}
  
  def lockWith[A <% Lock, B](l: A)(e: => B) = {
    l.lock()
    try e finally l.unlock()
  }
  
  def readLockWith[A <% ReadWriteLock, B](l: A)(e: => B) =
    lockWith(l.readLock)(e)
  
  def writeLockWith[A <% ReadWriteLock, B](l: A)(e: => B) =
    lockWith(l.writeLock)(e)
}

使い方

  import LoanPatternLock._
  
  val lock = new java.util.concurrent.locks.ReentrantReadWriteLock
  
  val a = readLockWith(lock) {
    // どんな処理でもどんとこい
  }

オブジェクトで書いていますが、こういうパターンは自分の使っているエディタのコピペテンプレートに入れ、使いたいメソッドやクラスに貼付けることになります。

Glazed Lists を使っていて、ロックミスで油断しているとたまーにハマって悲しいのですが、ロックのバグはどこで起きたか、何が原因なのかが本当にわかりづらいです。個人用 PC でもマルチコアは普通な時代なので、デスクトップアプリケーション開発での平行処理はもはや必須です。なのできっとまたそのうち泣きを見ることになると思います。南無。

参考: マルチコア・システムでの Java 並行性バグのパターン