Этим вечером я посетил кувшин в
Денвере, где
Венкат Субраманиам говорил о
Скале . К сожалению, я прибыл на полпути через его
лекцию о программировании Scala и не получил возможности узнать столько, сколько хотел. То, что я увидел, сделало Scala очень мощным и (возможно) более легким в освоении, чем Java. Ниже приведены мои заметки из выступления Венката.
Параллельность важна в наши дни, потому что мы находимся в мире нескольких процессоров. Если у вас одновременно запущено несколько потоков, это может стать болезненным. До Java вам приходилось изучать API для многопоточности для каждой отдельной платформы. С Java «Пишите один раз, отлаживайте везде», вам нужно было изучить только один API. К сожалению, это довольно низкий уровень: как запустить поток, управлять им, остановить его и т. Д. Вы также должны помнить, куда поместить синхронизацию в вашем коде.
Благодаря Scala неизменяемость и ее действующие лица упрощают программирование параллельных систем. Например, вот веб-сервис, который получает цены на акции в последовательном порядке:
def getyearEndClosing(symbol : String, year : Int) = {
val url = "http://ichart.finance.yahoo.com/table.csv?s=" +
symbol + "&a=11&b=01&c" + year + "&d=11&e=31&f=" + year + "&g=m"
val data = io.Source.fromURL(url).mkString
val price = data.split("\n")(1).split(",")(4).toDouble
Thread.sleep(1000); // slow down internet
(symbol, price)
}
val symbols = List("APPL", "GOOG", "IBM", "JAVA", "MSFT")
val start = System.nanoTime
val top = (("", 0.0) /: symbols) { (topStock, symbol) =>
val (sym, price) = getYearEndClosing(symbol, 2008)
if (topStock._2 < price) (sym, price) else topStock
}
val end = System.nanoTime
println("Top stock is " + top._1 + " with price " + top._2)
println("Time taken " + (end - start)/10000000000.0)
Чтобы сделать это одновременно, мы создаем актеров . Актеры — это не что иное, как потоки со встроенной очередью сообщений. Актеры позволяют порождать отдельные потоки для извлечения каждой цены акций. Вместо того, чтобы делать:
symbols.foreach { symbol =>
getYearEndClosing(symbol, 2008)
}
Вы можете добавить актеров:
val caller = self
symbols.foreach { symbol =>
actor { caller ! getYearEndClosing(symbol, 2008) }
}
Затем удалите val (sym, price) = getYearEndClosing (symbol, 2008) и замените его следующим:
receive {
case(sym: String, price: Double) =>
if (topStock._2 < price) (sym, price) else topStock
}
После внесения этого изменения время выполнения кода сократилось с ~ 7 до ~ 2 секунд. Кроме того, поскольку в этом коде нет ничего изменчивого, вам не нужно беспокоиться о проблемах параллелизма.
С Scala вы не страдаете от проблем множественного наследования, которые возникают в Java. Вместо этого вы можете использовать Черты, чтобы делать миксины. Например:
import scala.actors._
import Actor._
class MyActor extends Actor {
def act() {
for(i <- 1 to 3) {
receive {
case msg => println("Got " + msg)
}
}
}
When extending Actor, you have to call MyActor.start to start the Actor. Writing actors this way is not recommended (not sure why, guessing because you have to manually start them).
Venkat is now showing an example that counts prime numbers and he’s showing us how it pegs the CPU when counting how many exist between 1 and 1 million (78,499). After adding actor and receive logic, he shows how his Activity Monitor shows 185% CPU usage, indicating that both cores are being used.
What happens when one of the threads crashes and burns? The receive will wait forever. Because of this, using receive is a bad idea. It’s much better to use receiveWithin(millis) to set a timeout. Then you can catch the timeout in the receiveWithin block using:
case TIMEOUT => println("Uh oh, timed out")
A more efficient way to use actors is using react instead of receive. With react, threads leave after putting the message on the queue and new threads are started to execute the block when the message is «reacted» to. One thing to remember with react is any code after the react block will never be executed. Just like receiveWithin(millis), you can use reactWithin(millis) to set a timeout.
The major thing I noticed between receive and react is Venkat often had to change the method logic to use react. To solve this, you can use loop (or better yet, loopWhile(condition)) to allow accessing the data outside the react block. In conclusion, reactWithin(millis) is best to use, unless you need to execute code after the react block.
Conclusion
This was a great talk by Venkat. He used TextMate the entire time to author and execute all his Scala examples. Better yet, he never used any sort of presentation. All he had was a «todo» list with topics (that he checked off as he progressed) and a sample.scala file.
Personally, I don’t plan on using Scala in the near future, but that’s mostly because I’m doing UI development and GWT and JavaScript are my favorite languages for that. On the server-side, I can see how it reduces the amount of Java you need to write (the compiler works for you instead of you working for the compiler). However, my impression is its sweet spot is when you need to easily author an efficient concurrent system.
If you’re looking to learn Scala, I’ve heard Scala by Example (PDF) is a great getting-started resource. From there, I believe Programming in Scala and Venkat’s Programming Scala are great books.
From http://raibledesigns.com/rd/entry/concurrency_on_the_jvm_using