Scala: первые шаги в акторах

Многопоточное программирование является немаловажным в данный момент. Практически любой современный язык знает такие понятия как потоки, процессы и тому подобное. К тематике многопоточного программирования также относятся так называемые акторы. Дабы не переговаривать некоторые вещи на новый лад, процитирую понятие актора с Википедии:

В компьютерных науках модель акторов представляет собой математическую модель параллельных вычислений, которая трактует понятие «актор» как универсальный примитив параллельного численного расчёта: в ответ на сообщения, которые он получает, актор может принимать локальные решения, создавать новые акторы, посылать свои сообщения, а также устанавливать, как следует реагировать на последующие сообщения.

Что ж, суть вполне понятна и проста. Попробуем написать набор из акторов, один из которых по запросу генерирует следующее число Фибоначчи, а другой запрашивает генерацию.

Подготовка

Для начала импортируем необходимые классы:

Sample code
1
2
import scala.actors.Actor
import scala.actors.Actor._

Затем опишем сообщения, которые могут пересылать акторы:

Sample code
1
2
3
case object Ready // По этому сообщению актор будет запрашивать новое число
case object Next // По этому сообщению актор будет вычислять новое число
case object Stop // По этому сообщению актор будет завершать свою работу

Актор Generator

Данный актор будет вычислять новые числа Фибоначчи. Для того, чтобы это действительно был актор, нам нужно унаследовать его от scala.actors.Actor и реализовать функцию def act() { .. }. Внутри функция должна иметь конструкцию receive { .. }, которая и займется обработкой сообщений:

Sample code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class Generator extends Actor{
def act() {
var a = 1
var b = 1
while (true) {
// Ждем новое сообщение
receive {
// Получили сообщение Next
case Next => {
println(a)
val c = a + b
a = b
b = c
// Ответили отправителю сообщением Ready
sender ! Ready
}
// Получили сообщение Stop
case Stop => {
println("Enough!")
// Ответили отправителю тем же и остановили актор
sender ! Stop
exit()
}
}
}
}
}

Актор Sender

Данный актор будет иметь схожую с предыдущим актором архитектуру, потому приведу просто его код с некоторыми пояснениями:

Sample code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Sender(calc: Actor, maxCount: Int) extends Actor {
def act() {
var counter = 0
// В конструкторе нам был передан другой актор, которому мы сразу отправим сообщение
calc ! Next
while (true) {
receive {
// Если актор-отправитель уже готов
case Ready => {
counter += 1
// Если посчитали слишком мало чисел Фибоначчи
if (counter < maxCount) {
// То просим следующее
calc ! Next
} else {
// Иначе останавливаем актор
calc ! Stop
exit()
}
}
}
}
}
}

Запуск

Для всего этого нам понадобится только несколько строчек:

Sample code
1
2
3
4
5
6
// Создаем экземпляры акторов
val generator = new Generator
val sender = new Sender(generator, 10)
//Запускаем их
generator.start()
sender.start()

Также я попробовал запускать акторы в другом порядке - сначала Sender, а потом Generator, но даже так все отработало прекрасно.

Заключение

Все же Scala предлагает мощную поддержку многопоточности “из коробки”. Уже после того, как я “поиграл” с акторами, я выяснил, что текущая версия Scala имеет немного другую систему, которая разительно отличается от той, с которой я впервые познакомился. Также несомненно радует наличие таких вещей, как реакторы, расписания, каналы и др. В целом это во много раз лучше, чем создавать громоздкую конструкцию из потоков на чистой Java.

Ссылки