什么是最好的Scala线程安全方式来写入BufferedWriter?

4
我有一个简单的方法,它会异步地写入一行数据到一个文件中,并在该行后面紧接着换行符。
  def writeToFile(bw: BufferedWriter, str: String) = {
    bw.write(str)
    bw.newLine
  }

当我的程序运行时,由于调用的异步性质,文件中的行会出现“混乱”。例如...假设writeToFile(bw,“foo”)被异步地执行了3次,我可能会得到:
正确的输出
foo foo foo
可能的错误输出
foofoo foo
我可以通过使用像这样的synchronized方法来避免这种可能性:
  def writeToFile(bw: BufferedWriter, str: String) = synchronized {
    bw.write(str)
    bw.newLine
  }

我研究发现,我无法确定在扩展我的应用程序时这种方法有多“安全”。 我能找到的唯一使用synchronized的示例是访问集合时,而不是写入文件。我的应用程序是基于Play! Framework 2.4.2构建的。


2
你是正确的,synchronized 是 JVM 设计的锁定函数调用的方式。然而,这并不意味着如果你有其他线程尝试使用它,在那段时间内传递的任何 BufferedWriter 都被保护,因此最好将该对象进行包装和隐藏。参考此链接以获取更多信息:https://twitter.github.io/scala_school/concurrency.html - LaloInDublin
1个回答

8
我个人会为每个BufferedWriter创建一个Akka Actor,以完全封装它。
import java.io.BufferedWriter
import akka.actor._
import playground.BufferedWriterActor.WriteToBuffer

object BufferedWriterActor {
  val name = "BufferedWriterActor"
  def props(bw: BufferedWriter) = Props(classOf[BufferedWriterActor], bw)

  case class WriteToBuffer(str: String)
}

class BufferedWriterActor(bw: BufferedWriter) extends Actor {

  def receive: Actor.Receive = {
    case WriteToBuffer(str) =>
      bw.write(str)
      bw.newLine()
  }
}

像这样使用:

import akka.actor.{ActorSystem, Props}

object HelloWorld {
  def main(args: Array[String]): Unit = {
    val system = ActorSystem("mySystem")

    // Share this actor across all your threads.
    val myActor = system.actorOf(BufferedWriterActor.props(bw), BufferedWriterActor.name)

    // Send messages to this actor from all you threads.
    myActor ! BufferedWriterActor.WriteToBuffer("The Text")
  }
}

这将在单个线程中链接所有对该缓冲区的调用。
有关akka及其actor的更多信息,请参见此处:

http://akka.io/

http://doc.akka.io/docs/akka/snapshot/scala/actors.html

同时,Play框架本身也使用Akka,因此您应该能够使用其默认的ActorSystem,但我不记得具体细节了,抱歉。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接