ExecutionContext.global和主线程之间的区别

时间:2017-11-30 16:38:23

标签: multithreading scala scala-cats

我有以下代码:

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 之间有什么区别?

如果这两个不同,如何获取主线程?

1 个答案:

答案 0 :(得分:2)

  

运行上面的代码,为什么应用程序永远不会被终止?

这个额外的问题对于评论来说太大了,所以这是我的答案。

问题是在JVM中,所有Thread被分为“普通”和"daemon"个线程。这里最重要的是

  

当运行的唯一线程都是守护程序线程时,Java虚拟机退出。

因此,如果您有任何正在运行的非守护进程Thread,JVM认为您的应用程序仍在工作,即使它实际上什么都不做(可能只是等待一些输入)。 “主”线程显然是一个“正常”线程。标准ExecutionContext.global创建的线程是守护进程,因此不会阻止您的应用程序在主线程完成时退出。 Java Executors.newCachedThreadPool创建的线程是非守护进程,因此可以使应用程序保持活动状态。有几种可能的解决方案:

  1. 除了ExecutionContext之外,请勿使用其他global,即根本不使用Executors.newCachedThreadPool。根据您的情况,这可能与您想要的不同。

  2. 完成所有工作后,明确shutdown您的自定义ExecutorService。这里要小心,因为shutdown不等待所有活动任务完成。所以代码应该变成类似

  3. 的东西
    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()
    
    1. 使用创建守护程序线程的自定义池。例如,您可以这样做:
    2. 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方式。