在主线程上等待回调方法
Waiting on main thread for callback methods
我是 Scala 的新手,关注 Scala Book Concurrency 部分(来自 docs.scala-lang.org)。基于他们在书中给出的示例,我编写了一个非常简单的代码块来使用 Futures 进行测试:
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global
import scala.util.{Failure, Success}
object Main {
def main(args: Array[String]): Unit = {
val a = Future{Thread.sleep(10*100); 42}
a.onComplete {
case Success(x) => println(a)
case Failure(e) => e.printStackTrace
}
Thread.sleep(5000)
}
}
编译后运行,正确打印出来:
Future(Success(42))
到控制台。我无法理解为什么 Thread.sleep()
调用是在 onComplete 回调方法之后进行的。直觉上,至少对我来说,会在回调之前调用 Thread.sleep()
,所以当主线程到达 onComplete
方法时,a
被分配了一个值。如果我将 Thread.sleep()
调用移动到 a.onComplete
之前,控制台不会打印任何内容。我可能想多了,但如果能帮助澄清,我将不胜感激。
Scala 中的很多东西都是函数,不一定看起来像函数。 onComplete
的参数就是其中之一。你写的是
a.onComplete {
case Success(x) => println(a)
case Failure(e) => e.printStackTrace
}
毕竟 Scala 的魔力是有效的(模 PartialFunction
恶作剧)
a.onComplete({ value =>
value match {
case Success(x) => println(a)
case Failure(e) => e.printStackTrace
}
})
onComplete
实际上没有做任何工作。它只是设置一个将在以后调用的函数。所以我们希望尽快做到这一点,Scala 调度程序(具体来说,ExecutionContext
)将在方便时调用我们的回调。
注册回调后使用Thread.sleep()
时
a.onComplete {
case Success(x) => println(a)
case Failure(e) => e.printStackTrace
}
Thread.sleep(5000)
然后正在执行未来主体的线程有时间休眠一秒钟,并将 42
设置为未来成功执行的结果。到那时(大约 1 秒后),onComplete
回调已经注册,因此线程也会调用它,您会在控制台上看到输出。
序列本质上是:
- t = 0: 守护线程开始计算
42
- t = 0: 主线程创建并注册回调。
- t = 1: 守护线程完成
42
的计算
- t = 1 + eps: 守护线程找到注册的回调并用结果调用它
Success(42)
.
- t = 5: 主线程终止
- t = 5 + eps: 程序停止。
(我非正式地使用 eps
作为一些相当小的时间间隔的占位符;+ eps
的意思是“几乎紧接着”。)
如果交换 a.onComplete
和外部 Thread.sleep
,如
Thread.sleep(5000)
a.onComplete {
case Success(x) => println(a)
case Failure(e) => e.printStackTrace
}
然后执行 future 主体的线程将在一秒后计算结果 42
,但它不会看到任何已注册的回调(它必须再等四秒直到回调被执行)在主线程上创建和注册)。但是一旦过了 5 秒,主线程注册回调并立即退出。即使到那时它有机会知道结果 42
已经被计算出来,主线程也不会尝试执行回调,因为它是它的业务 none(这就是线程在执行上下文中是为了)。因此,在注册回调后,主线程立即退出。有了它,线程池中的所有守护线程都被杀掉,程序退出,让你在控制台看不到任何东西。
通常的事件顺序大致是这样的:
- t = 0: 守护线程开始计算
42
- t = 1: 守护线程完成了
42
的计算,但无法对其执行任何操作。
- t = 5: 主线程创建并注册回调
- t = 5 + eps: 主线程终止,守护线程被杀死,程序停止。
这样(几乎)没有时间守护线程可以唤醒、找到回调并调用它。
我是 Scala 的新手,关注 Scala Book Concurrency 部分(来自 docs.scala-lang.org)。基于他们在书中给出的示例,我编写了一个非常简单的代码块来使用 Futures 进行测试:
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global
import scala.util.{Failure, Success}
object Main {
def main(args: Array[String]): Unit = {
val a = Future{Thread.sleep(10*100); 42}
a.onComplete {
case Success(x) => println(a)
case Failure(e) => e.printStackTrace
}
Thread.sleep(5000)
}
}
编译后运行,正确打印出来:
Future(Success(42))
到控制台。我无法理解为什么 Thread.sleep()
调用是在 onComplete 回调方法之后进行的。直觉上,至少对我来说,会在回调之前调用 Thread.sleep()
,所以当主线程到达 onComplete
方法时,a
被分配了一个值。如果我将 Thread.sleep()
调用移动到 a.onComplete
之前,控制台不会打印任何内容。我可能想多了,但如果能帮助澄清,我将不胜感激。
Scala 中的很多东西都是函数,不一定看起来像函数。 onComplete
的参数就是其中之一。你写的是
a.onComplete {
case Success(x) => println(a)
case Failure(e) => e.printStackTrace
}
毕竟 Scala 的魔力是有效的(模 PartialFunction
恶作剧)
a.onComplete({ value =>
value match {
case Success(x) => println(a)
case Failure(e) => e.printStackTrace
}
})
onComplete
实际上没有做任何工作。它只是设置一个将在以后调用的函数。所以我们希望尽快做到这一点,Scala 调度程序(具体来说,ExecutionContext
)将在方便时调用我们的回调。
注册回调后使用Thread.sleep()
时
a.onComplete {
case Success(x) => println(a)
case Failure(e) => e.printStackTrace
}
Thread.sleep(5000)
然后正在执行未来主体的线程有时间休眠一秒钟,并将 42
设置为未来成功执行的结果。到那时(大约 1 秒后),onComplete
回调已经注册,因此线程也会调用它,您会在控制台上看到输出。
序列本质上是:
- t = 0: 守护线程开始计算
42
- t = 0: 主线程创建并注册回调。
- t = 1: 守护线程完成
42
的计算
- t = 1 + eps: 守护线程找到注册的回调并用结果调用它
Success(42)
. - t = 5: 主线程终止
- t = 5 + eps: 程序停止。
(我非正式地使用 eps
作为一些相当小的时间间隔的占位符;+ eps
的意思是“几乎紧接着”。)
如果交换 a.onComplete
和外部 Thread.sleep
,如
Thread.sleep(5000)
a.onComplete {
case Success(x) => println(a)
case Failure(e) => e.printStackTrace
}
然后执行 future 主体的线程将在一秒后计算结果 42
,但它不会看到任何已注册的回调(它必须再等四秒直到回调被执行)在主线程上创建和注册)。但是一旦过了 5 秒,主线程注册回调并立即退出。即使到那时它有机会知道结果 42
已经被计算出来,主线程也不会尝试执行回调,因为它是它的业务 none(这就是线程在执行上下文中是为了)。因此,在注册回调后,主线程立即退出。有了它,线程池中的所有守护线程都被杀掉,程序退出,让你在控制台看不到任何东西。
通常的事件顺序大致是这样的:
- t = 0: 守护线程开始计算
42
- t = 1: 守护线程完成了
42
的计算,但无法对其执行任何操作。 - t = 5: 主线程创建并注册回调
- t = 5 + eps: 主线程终止,守护线程被杀死,程序停止。
这样(几乎)没有时间守护线程可以唤醒、找到回调并调用它。