#scala #concurrency
#scala #параллелизм
Вопрос:
Я пытаюсь закодировать это упражнение с LeetCode для печати foo / bar поочередно в Scala, используя обычные Runnables с wait(), notifyAll(), но не могу заставить его выдавать желаемый результат, который должен быть:
foo bar foo bar foo bar foo bar foo bar
Вот код:
import scala.concurrent.ExecutionContext.Implicits.global
class Foo extends Runnable {
@Override def run(): Unit = { print("foo ") }
}
class Bar extends Runnable {
@Override def run(): Unit = { print("bar ") }
}
val printFoo = new Foo
val printBar = new Bar
class FooBar {
private var foosLoop: Boolean = false
@throws(classOf[InterruptedException])
def foo: Unit = for (_ <- 1 to 5) synchronized {
while (foosLoop) { wait() }
printFoo.run()
foosLoop = true
notifyAll()
}
@throws(classOf[InterruptedException])
def bar: Unit = for (_ <- 1 to 5) synchronized {
while (!foosLoop) { wait() }
printBar.run()
foosLoop = false
notifyAll()
}
}
val fb = new FooBar
fb.foo
fb.bar
// Output:
// foo <=== prints only first "foo "
Может кто-нибудь помочь мне понять, что я сделал не так?
Мой второй вопрос: может ли это быть реализовано с помощью Scala Futures, заменяющих Runnables?
Обновить:
Опубликованный код фактически работает до тех пор, пока fb.foo
и fb.bar
должен вызываться из отдельных потоков.
val tFoo = new Thread (new Runnable { @Override def run(): Unit = fb.foo })
val tBar = new Thread (new Runnable { @Override def run(): Unit = fb.bar })
tFoo.start()
tBar.start()
Комментарии:
1. Что вы уже пробовали сами?
Ответ №1:
Может кто-нибудь помочь мне понять, что я сделал не так?
Понятия не имею, я не использовал Runnables в своей жизни, и они не используются в Scala.
(и я бы сказал, что они также больше не используются в Java)
Может ли это быть реализовано с помощью Scala Futures, заменяющих Runnables?
Да, что-то вроде этого:
import java.util.concurrent.Semaphore
import scala.concurrent.{ExecutionContext, Future}
object RunAlternately {
/**
* Runs two taks concurrently and alternating between the two.
* @param n the amout of times to run each task.
* @param aTaks the first task.
* @param bTaks the second task.
*/
def apply(n: Int)(aTask: => Unit)(bTask: => Unit)(implicit ec: ExecutionContext): Future[Unit] ={
val aLock = new Semaphore(1)
val bLock = new Semaphore(0)
def runOne(task: => Unit, thisLock: Semaphore, thatLock: Semaphore): Future[Unit] =
Future {
var i = 0
while (i < n) {
thisLock.acquire()
task
thatLock.release()
i = 1
}
}
val aFuture = runOne(aTask, thisLock = aLock, thatLock = bLock)
val bFuture = runOne(bTask, thisLock = bLock, thatLock = aLock)
aFuture.flatMap(_ => bFuture)
}
}
Посмотрите, как он работает здесь.
Однако такого рода вещи обычно лучше моделируются с помощью API еще более высокого уровня, таких как IO или Streams.