我有以下代码:
var table = document.getElementById("table-div");
var latestKnownScrollTop = 0;
var ticking = false;
function onTableScroll() {
latestKnownScrollTop = table.scrollTop;
requestTick();
}
function requestTick() {
if (!ticking) {
requestAnimationFrame(tableUpdate);
}
ticking = true;
}
function tableUpdate() {
ticking = false;
var currentScrollTop = latestKnownScrollTop;
var translate = "translate(0," + currentScrollTop + "px)";
table.querySelector("thead").style.transform = translate;
}
table.addEventListener("scroll", onTableScroll);
,输出为:
object KafkaApi {
private implicit val main: ExecutionContextExecutor = ExecutionContext.global
private val workers = ExecutionContext.fromExecutor(Executors.newCachedThreadPool())
def main(args: Array[String]) {
foo.unsafeRunAsync(_ => ())
//foo.unsafeRunSync()
println("Hello")
}
def foo: IO[Unit] =
for {
_ <- IO {
println(Thread.currentThread().getName)
}
_ <- IO.shift(workers)
_ <- IO {
println(Thread.currentThread().getName)
}
_ <- IO {
println(Thread.currentThread().getName)
}
_ <- IO {
println(Thread.currentThread().getName)
}
_ <- IO.shift(main)
_ <- IO {
println(Thread.currentThread().getName)
}
_ <- IO {
println(Thread.currentThread().getName)
}
_ <- IO {
println(Thread.currentThread().getName)
}
} yield ()
}
主要和 scala-execution-context-global-14 之间有什么区别?
如果这两个不同,如何获取主线程?
答案 0 :(得分:2)
运行上面的代码,为什么应用程序永远不会被终止?
这个额外的问题对于评论来说太大了,所以这是我的答案。
问题是在JVM中,所有Thread
被分为“普通”和"daemon"个线程。这里最重要的是
当运行的唯一线程都是守护程序线程时,Java虚拟机退出。
因此,如果您有任何正在运行的非守护进程Thread
,JVM认为您的应用程序仍在工作,即使它实际上什么都不做(可能只是等待一些输入)。 “主”线程显然是一个“正常”线程。标准ExecutionContext.global
创建的线程是守护进程,因此不会阻止您的应用程序在主线程完成时退出。 Java Executors.newCachedThreadPool
创建的线程是非守护进程,因此可以使应用程序保持活动状态。有几种可能的解决方案:
除了ExecutionContext
之外,请勿使用其他global
,即根本不使用Executors.newCachedThreadPool
。根据您的情况,这可能与您想要的不同。
完成所有工作后,明确shutdown
您的自定义ExecutorService
。这里要小心,因为shutdown
不等待所有活动任务完成。所以代码应该变成类似
private val pool = Executors.newCachedThreadPool
implicit private val workers = ExecutionContext.fromExecutor(pool)
// do whatever you want with workers
// optionally wait for all the work to be done
pool.shutdown()
val workers = ExecutionContext.fromExecutor(Executors.newCachedThreadPool(new ThreadFactory {
private val defaultDelegate = Executors.defaultThreadFactory()
override def newThread(r: Runnable): Thread = {
val t = defaultDelegate.newThread(r)
//let the default factory do all the job and just override daemon-flag
t.setDaemon(true)
t
}
}))
恕我直言,#2和#3之间的主要 权衡 是方便与正确。在#3中,您不必考虑所有任务的完成位置,因此可以安全地调用shutdown
,这很方便。价格是,如果由于某种原因你错误判断并且你的“主”线程在所有其他任务完成之前退出,你将不会知道出现任何问题,因为守护程序线程将被默默地杀死。如果您使用#2并且执行相同的错误,如果您不在该代码路径中调用shutdown
,您的应用程序将继续运行,或者您会在日志中看到某些警告池已关闭还有一些任务正在进行中。因此,如果这只是一个长序列处理的中间步骤,由于某种原因需要自定义线程池,我可能会选择#3;但如果这种并行执行是主要行为,那么我会采用更明确的#2方式。