Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the best Scala thread-safe way to write to a BufferedWriter?

I have a simple method that writes a line of data to a File followed by a new line that is executed asynchronously.

  def writeToFile(bw: BufferedWriter, str: String) = {
    bw.write(str)
    bw.newLine
  }

When my program runs I'm getting "mixed up" rows in the file due to the async nature of the calls. For instance...say writeToFile(bw, "foo") is executed 3 times asynchronously I may get:

correct output

foo

foo

foo

possible incorrect output

foofoo

foo

I'm able to avoid this possibility by using synchronized method like this:

  def writeToFile(bw: BufferedWriter, str: String) = synchronized {
    bw.write(str)
    bw.newLine
  }

From what I researched I can't determine how "safe" this is in regards to scaling my application. The only examples I can find using synchronized is when accessing collections, not writing to a file. My application is built in the Play! Framework 2.4.2.

like image 423
ChickenSniper Avatar asked Jan 06 '23 20:01

ChickenSniper


1 Answers

I personally would create an akka actor for each BufferedWriter what will encapsulate it completely.

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()
  }
}

Use it like this:

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")
  }
}

This will chain all calls to this buffer in a single thread.

More info on akka and its actors is here:

http://akka.io/

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

Also play framework itself uses akka so you should be able to use its default ActorSystem, but I do not remember how exactly, sorry.

like image 186
Yury Sukhoverkhov Avatar answered Jan 20 '23 08:01

Yury Sukhoverkhov