Scala:管理期货顺序执行的酷方法?
我正在尝试用Scala编写一个数据模块 在并行加载整个数据时,一些数据依赖于其他数据,因此必须以有效的方式管理执行序列 例如,在代码中,我保留了一个带有数据和清单名称的映射Scala:管理期货顺序执行的酷方法?,scala,future,Scala,Future,我正在尝试用Scala编写一个数据模块 在并行加载整个数据时,一些数据依赖于其他数据,因此必须以有效的方式管理执行序列 例如,在代码中,我保留了一个带有数据和清单名称的映射 val dataManifestMap = Map( "foo" -> manifest[String], "bar" -> manifest[Int], "baz" -> manifest[Int], "foobar" -> manifest[Set[String]], // nee
val dataManifestMap = Map(
"foo" -> manifest[String],
"bar" -> manifest[Int],
"baz" -> manifest[Int],
"foobar" -> manifest[Set[String]], // need to be executed after "foo" and "bar" is ready
"foobarbaz" -> manifest[String], // need to be executed after "foobar" and "baz" is ready
)
这些数据将存储在可变哈希映射中
private var dataStorage = new mutable.HashMap[String, Future[Any]]()
有一些代码将加载数据
def loadAllData(): Future[Unit] = {
Future.join(
(dataManifestMap map {
case (data, m) => loadData(data, m) } // function has all the string matching and loading stuff
).toSeq
)
}
def loadData[T](data: String, m: Manifest[T]): Future[Unit] = {
val d = data match {
case "foo" => Future.value("foo")
case "bar" => Future.value(3)
case "foobar" => // do something with dataStorage("foo") and dataStorage("bar")
... // and so forth (in a real example it would be much more complicated for sure)
}
d flatMap {
dVal => { this.synchronized { dataStorage(data) = dVal }; Future.value(Unit) }
}
}
这样,我无法确保在“foo”和“bar”准备就绪时加载“foobar”,以此类推
既然我可能有数百个不同的数据,我如何以“酷”的方式管理它
如果我能有一种数据结构,它包含了关于某件事情的所有信息,必须在某件事情之后加载,并且flatMap可以以一种简洁的方式处理顺序执行,那将是“很棒的”
提前感谢您的帮助。在所有条件相同的情况下,我倾向于使用
进行理解。例如:
def findBucket: Future[Bucket[Empty]] = ???
def fillBucket(bucket: Bucket[Empty]): Future[Bucket[Water]] = ???
def extinguishOvenFire(waterBucket: Bucket[Water]): Future[Oven] = ???
def makeBread(oven: Oven): Future[Bread] = ???
def makeSoup(oven: Oven): Future[Soup] = ???
def eatSoup(soup: Soup, bread: Bread): Unit = ???
def doLunch = {
for (bucket <- findBucket;
filledBucket <- fillBucket(bucket);
oven <- extinguishOvenFire(filledBucket);
soupFuture = makeSoup(oven);
breadFuture = makeBread(oven);
soup <- soupFuture;
bread <- breadFuture) {
eatSoup(soup, bread)
}
}
然后它将返回Future[coven]
——如果您想在午餐后用烤箱做其他事情,这可能会很有用
至于你的代码,我的第一个应该是你应该考虑的,因为它看起来可能符合你的要求。如果没有,我的下一个想法将是替换您当前拥有的接口,并使用基于类型化方法调用的内容:
private def save[T](key: String)(value: Future[T]) = this.synchronized {
dataStorage(key) = value
value
}
def loadFoo = save("foo"){Future("foo")}
def loadBar = save("bar"){Future(3)}
def loadFooBar = save("foobar"){
for (foo <- loadFoo;
bar <- loadBar) yield foo + bar // Or whatever
}
def loadBaz = save("baz"){Future(200L)}
def loadAll = {
val topLevelFutures = Seq(loadFooBar, loadBaz)
// Use standard library function to combine futures
Future.fold(topLevelFutures)(())((u,f) => ())
}
// I don't consider this method necessary, but if you've got a legacy API to support...
def loadData[T](key: String)(implicit manifest: Manifest[T]) = {
val future = key match {
case "foo" => loadFoo
case "bar" => loadBar
case "foobar" => loadFooBar
case "baz" => loadBaz
case "all" => loadAll
}
future.mapTo[T]
}
private def save[T](键:字符串)(值:Future[T])=this.synchronized{
数据存储(键)=值
价值
}
def loadFoo=save(“foo”){Future(“foo”)}
def loadBar=save(“bar”){Future(3)}
def loadFooBar=保存(“foobar”){
对于(foo loadFoo)
案例“bar”=>加载条
案例“foobar”=>loadFooBar
案例“baz”=>loadBaz
案例“全部”=>loadAll
}
future.mapTo[T]
}
在所有条件相同的情况下,我倾向于使用来理解。例如:
def findBucket: Future[Bucket[Empty]] = ???
def fillBucket(bucket: Bucket[Empty]): Future[Bucket[Water]] = ???
def extinguishOvenFire(waterBucket: Bucket[Water]): Future[Oven] = ???
def makeBread(oven: Oven): Future[Bread] = ???
def makeSoup(oven: Oven): Future[Soup] = ???
def eatSoup(soup: Soup, bread: Bread): Unit = ???
def doLunch = {
for (bucket <- findBucket;
filledBucket <- fillBucket(bucket);
oven <- extinguishOvenFire(filledBucket);
soupFuture = makeSoup(oven);
breadFuture = makeBread(oven);
soup <- soupFuture;
bread <- breadFuture) {
eatSoup(soup, bread)
}
}
然后它将返回Future[coven]
——如果您想在午餐后用烤箱做其他事情,这可能会很有用
至于你的代码,我的第一个将是你应该考虑的,因为它看起来可能符合你的要求。如果不是,我下一个想法是替换你当前得到的接口,并根据基于类型化方法调用的东西:
private def save[T](key: String)(value: Future[T]) = this.synchronized {
dataStorage(key) = value
value
}
def loadFoo = save("foo"){Future("foo")}
def loadBar = save("bar"){Future(3)}
def loadFooBar = save("foobar"){
for (foo <- loadFoo;
bar <- loadBar) yield foo + bar // Or whatever
}
def loadBaz = save("baz"){Future(200L)}
def loadAll = {
val topLevelFutures = Seq(loadFooBar, loadBaz)
// Use standard library function to combine futures
Future.fold(topLevelFutures)(())((u,f) => ())
}
// I don't consider this method necessary, but if you've got a legacy API to support...
def loadData[T](key: String)(implicit manifest: Manifest[T]) = {
val future = key match {
case "foo" => loadFoo
case "bar" => loadBar
case "foobar" => loadFooBar
case "baz" => loadBaz
case "all" => loadAll
}
future.mapTo[T]
}
private def save[T](键:字符串)(值:Future[T])=this.synchronized{
数据存储(键)=值
价值
}
def loadFoo=save(“foo”){Future(“foo”)}
def loadBar=save(“bar”){Future(3)}
def loadFooBar=保存(“foobar”){
对于(foo loadFoo)
案例“bar”=>加载条
案例“foobar”=>loadFooBar
案例“baz”=>loadBaz
案例“全部”=>loadAll
}
future.mapTo[T]
}
可能正在使用Actors会更切中要害吗?很抱歉使用了糟糕的sudo代码。另外,我需要的是确保在尝试加载“foobar”时“foo”和“bar”已准备就绪。因此,解决方案不一定需要与期货的顺序执行相关。它可以正确地使用承诺,也可以使用任何可以达到目的的东西。谢谢。这正是有状态的参与者可以发挥作用的地方。@Ashalynd谢谢你的建议!我仍然不确定应该使用什么样的设计:)也许在这里使用演员会更切中要害?对不起,sudo代码太糟糕了。另外,我所需要的是确保在尝试加载“foobar”时“foo”和“bar”已准备就绪。因此,解决方案不一定需要与期货的顺序执行相关。它可以正确地使用承诺,也可以使用任何可以达到目的的东西。谢谢。这正是有状态的参与者可以发挥作用的地方。@Ashalynd谢谢你的建议!我仍然不确定应该使用什么样的设计:)谢谢你的回答。我仍然不确定我是否应该实现您的设计或使用演员…也许您可以告诉我们更多关于您的问题的细节。例如,任务序列是否需要在运行时进行修改(可能由非技术用户修改),或者如果在编译时进行了修改,是否可以?是否可以通过检查数据来确定正确的顺序?我们实际上谈论的是什么样的数据,加载数据时使用的是什么样的代码?这些东西可能暗示了一种特定的设计。不,它不必是可修改的。顺序将是固定的。虽然,除了在开始时加载整个数据外,“foobar”也有可能在运行时单独重新加载(数据取决于其他数据),因此在这种情况下,在请求加载“foobar”时,必须事先加载“foo”和“bar”。2.检查数据对确定订单3没有帮助。数据将由Int、Map等组成。数据将从redis、MySQL和msgpack缓存数据加载。此时,所有代码都是用Scala编写的。(详细说明1)loadData[T]可以单独调用,而不仅仅是在loadAllData中。因此,当调用loadData(“foobar”)并且没有加载“foo”和“bar”时,我们应该在加载“foobar”之前同时加载“foo”和“bar”!希望这有帮助!这听起来有点像缓存。这样公平吗?你会考虑使用喷雾缓存之类的东西吗?谢谢你的回答。我仍然不确定我是否应该实现您的设计或使用演员…也许您可以告诉我们更多关于您的问题的细节。例如,任务序列是否需要在运行时进行修改(可能由非技术用户修改),或者如果在编译时进行了修改,是否可以?是否可以通过检查数据来确定正确的顺序?我们实际上谈论的是什么样的数据,加载数据时使用的是什么样的代码?这些东西可能暗示了一种特定的设计。不,它不必是可修改的。顺序将是固定的。虽然,除了在开始时加载整个数据外,“foobar”也有可能在运行时单独重新加载(数据取决于其他数据),因此在这种情况下,在请求加载“foobar”时,必须事先加载“foo”和“bar”。2.检查数据对确定订单3没有帮助。数据将