加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 综合聚焦 > 服务器 > 安全 > 正文

斯卡拉 – 为什么阻止未来被视为不良做法?

发布时间:2020-12-16 18:45:52 所属栏目:安全 来源:网络整理
导读:我试图理解声明背后的理性 ? For cases where blocking is absolutely necessary,futures can be blocked on (although it is discouraged) ForkJoinPool背后的想法是加入阻塞操作的进程,这是期货和演员的执行者上下文的主要实现.它应该有效阻止连接. 在这个
我试图理解声明背后的理性
? For cases where blocking is absolutely necessary,futures can be blocked on (although it is discouraged)

ForkJoinPool背后的想法是加入阻塞操作的进程,这是期货和演员的执行者上下文的主要实现.它应该有效阻止连接.

在这个非常简单的场景中,我写了一个小基准,看起来像旧式期货(scala 2.9)快2倍.

@inline
  def futureResult[T](future: Future[T]) = Await.result(future,Duration.Inf)

  @inline
  def futureOld[T](body: => T)(implicit  ctx:ExecutionContext): () => T = {
    val f = future(body)
    () => futureResult(f)
  }

  def main(args: Array[String]) {
    @volatile

    var res = 0d
    CommonUtil.timer("res1") {
      (0 until 100000).foreach {  i =>
       val f1 = futureOld(math.exp(1))
        val f2 = futureOld(math.exp(2))
        val f3 = futureOld(math.exp(3))
        res = res + f1() + f2() + f3()
      }
    }
    println("res1 = "+res)
    res = 0

    res = 0
    CommonUtil.timer("res1") {
      (0 until 100000).foreach {  i =>
        val f1 = future(math.exp(1))
        val f2 = future(math.exp(2))
        val f3 = future(math.exp(3))
        val f4 = for(r1 <- f1; r2 <- f2 ; r3 <- f3) yield r1+r2+r3
        res = res + futureResult(f4)
      }
    }
    println("res2 = "+res)
  }



start:res1
res1 - 1.683 seconds
res1 = 3019287.4850644027
start:res1
res1 - 3.179 seconds
res2 = 3019287.485058338

解决方法

适用于 ForkJoinTask报告的Java7文档:

A ForkJoinTask is a lightweight form of Future. The efficiency of
ForkJoinTasks stems from a set of restrictions (that are only
partially statically enforceable) reflecting their intended use as
computational tasks calculating pure functions or operating on purely
isolated objects. The primary coordination mechanisms are fork(),that
arranges asynchronous execution,and join(),that doesn’t proceed
until the task’s result has been computed. Computations should avoid
synchronized methods or blocks,and should minimize other blocking
synchronization apart from joining other tasks or using synchronizers
such as Phasers that are advertised to cooperate with fork/join
scheduling. Tasks should also not perform blocking IO,and should
ideally access variables that are completely independent of those
accessed by other running tasks. Minor breaches of these restrictions,
for example using shared output streams,may be tolerable in practice,
but frequent use may result in poor performance,and the potential to
indefinitely stall if the number of threads not waiting for IO or
other external synchronization becomes exhausted. This usage
restriction is in part enforced by not permitting checked exceptions
such as IOExceptions to be thrown. However,computations may still
encounter unchecked exceptions,that are rethrown to callers
attempting to join them. These exceptions may additionally include
RejectedExecutionException stemming from internal resource exhaustion,
such as failure to allocate internal task queues. Rethrown exceptions
behave in the same way as regular exceptions,but,when possible,
contain stack traces (as displayed for example using
ex.printStackTrace()) of both the thread that initiated the
computation as well as the thread actually encountering the exception;
minimally only the latter.

Doug Lea针对JSR166的maintenance repository(针对JDK8)扩展了以下内容:

A ForkJoinTask is a lightweight form of Future. The efficiency of
ForkJoinTasks stems from a set of restrictions (that are only
partially statically enforceable) reflecting their main use as
computational tasks calculating pure functions or operating on purely
isolated objects. The primary coordination mechanisms are fork(),that doesn’t proceed
until the task’s result has been computed. Computations should ideally
avoid synchronized methods or blocks,and should minimize other
blocking synchronization apart from joining other tasks or using
synchronizers such as Phasers that are advertised to cooperate with
fork/join scheduling. Subdividable tasks should also not perform
blocking I/O,and should ideally access variables that are completely
independent of those accessed by other running tasks. These guidelines
are loosely enforced by not permitting checked exceptions such as
IOExceptions to be thrown. However,computations may still encounter
unchecked exceptions,that are rethrown to callers attempting to join
them. These exceptions may additionally include
RejectedExecutionException stemming from internal resource exhaustion,
contain stack traces (as displayed for example using
ex.printStackTrace()) of both the thread that initiated the
computation as well as the thread actually encountering the exception;
minimally only the latter.

It is possible to define and use ForkJoinTasks that may block,but
doing do requires three further considerations: (1) Completion of few
if any other tasks should be dependent on a task that blocks on
external synchronization or I/O. Event-style async tasks that are
never joined (for example,those subclassing CountedCompleter) often
fall into this category. (2) To minimize resource impact,tasks should
be small; ideally performing only the (possibly) blocking action. (3)
Unless the ForkJoinPool.ManagedBlocker API is used,or the number of
possibly blocked tasks is known to be less than the pool’s
ForkJoinPool.getParallelism() level,the pool cannot guarantee that
enough threads will be available to ensure progress or good
performance.

TL;博士;

fork-join引用的“阻塞连接”操作不应与在任务中调用某些“阻塞代码”相混淆.

第一个是协调许多独立任务(不是独立的线程)来收集个人结果并评估整体结果.

第二个是关于在单个任务中调用潜在的长时间阻塞操作:例如通过网络进行IO操作,数据库查询,访问文件系统,访问全局同步的对象或方法……

Scala Futures和ForkJoinTasks都不鼓励第二种阻止.主要风险是线程池耗尽并且无法完成等待队列中的任务,而所有可用线程都忙于等待阻塞操作.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读