Van más (jobb) megoldás is (Java/C#/Scala esetén), mégpedig a Future-ök (C# esetén Task) használata.
A pszeudo kód:
message = openMessage // Megynitjuk/elkérjük az erőforrást
listOfFutures = useMessage(message) // Használjuk az erőforrást
listOfFutures.onComplete { closeMessage(message) } // Bezárjuk/visszaadjuk az erőforrást
Miért jobb?
- Nem függ az erőforrást tároló objektumok élettartamától az erőforrás lezárása.
- Az üzleti logika vezérli az erőforrás lezárását. Ebben az esetben kétféle lehet:
a) akkor kell azonnal lezárni az erőforrást, ha minden processHandler végzett,
b) akkor kell azonnal lezárni az erőforrást, ha valamelyik processHandler elsőként elszáll, mert pl. nem akarjuk, hogy a még futó processHandlerek hozzáférjenek az erőforráshoz.
Scala-ban írtam is egy kis példát erre:
import scala.concurrent._
import scala.concurrent.ExecutionContext.Implicits.global
import scala.util.{ Random, Try }
case class Message(number: Int)
object AsyncResource {
val processMessage = (waitSecond: Int) => (message: Message) => Future {
Try {
Thread.sleep(waitSecond * 1000)
if (Random.nextInt(6) == 1) throw new Exception("Crash")
s"Handled $waitSecond sec"
}
}
val openMessage = () => Message(Random.nextInt(100))
val closeMessage = (message: Message) => println(s"Close message: $message.")
val resource = () => {
val handlers = (1 to 5) map { i => processMessage(Random.nextInt(5)) }
val message = openMessage()
val processors = handlers map { handler => handler(message) }
val result = Future.sequence(processors)
result.onComplete { r => closeMessage(message); println(s"Result: $r") }
}
}
A resource függvényben van a teszt kód. 5 db processHandler függvényt teszek egy collection-be.
Mindegyik processHandler véletlenszerűen 0-4 mp-ig fut, és 1:6 valószínűséggel elszáll.
Nyitok egy Message erőforrást, feldolgoztatom a handlerekkel, majd ha mind végzett bezárom a Message erőforrást.
A példa kód az a) üzleti logika szerint működik. Ha kivesszük a Try szerkezetet a processMessage függvényből, akkor a b) szerint fog.
Példa kimenetek:
Close message: Message(78).
Result: Success(Vector(Failure(java.lang.Exception: Crash), Success(Handled 1 sec),
Failure(java.lang.Exception: Crash), Success(Handled 2 sec), Success(Handled 3 sec)))
Close message: Message(65).
Result: Success(Vector(Success(Handled 4 sec), Success(Handled 1 sec), Success(Handled 4 sec),
Success(Handled 2 sec), Success(Handled 3 sec)))