并发编程(待补充)

Scala的actor提供了一种基于事件的轻量级线程。只要使用scala.actors.Actor伴生对象的actor()方法,就可以创建一个actor。它接受一个函数值/闭包做参数,一创建好就开始运行。用!()方法给actor发消息,用receive()方法从actor接收消息。receive()也可以闭包为参数,通常用模式匹配处理接收到的消息。

我们看个例子,假定我们需要判定一个给定的数是完全数:

    def sumOfFactors(number: Int) = {
        (0 /: (1 to number)) { (sum, i) => if (number % i == 0) sum + i else sum }
    }

    def isPerfect(candidate: Int) = 2 * candidate == sumOfFactors(candidate)

这段代码按顺序计算了给定candidate数的因子之和。这段代码有个问题。如果数值很大,顺序执行会非常慢。而且,如果在多核处理器上运行这段代码,也利用不到多核的优势。不管在什么时候,都是一个核做了所有艰苦的工作,没有用到其他核。
对这种运算密集型操作而言,在任意时刻,都只有一个核有效地利用了起来。或者,可以将其视为双核的能力只用到一半。

将因子求和的计算划分到多线程上,可以获得更好的吞吐量。即便在只有一个处理器的机器上,应用也能获得更多的执行机会,得到更好的响应。

这样,将从1到candidate数这个范围内的数划分成多个区间,把每个区间内求和的任务分配给单独的线程。

import scala.actors.Actor._

object ActorTest {
    def sumOfFactorInRange(lower: Int, upper: Int, number: Int) = {
        (0 /: (lower to upper)) { (sum, i) => if (number % i == 0) sum + i else sum }
    }

    def isPerfectConcurrent(candidate: Int) = {
        val RANGE = 1000000
        val numberOfPartitions = (candidate.toDouble / RANGE).ceil.toInt
        val caller = self

        for (i <- 0 until numberOfPartitions) {
            val lower = i * RANGE + 1
            val upper = candidate min (i + 1) * RANGE

            actor {
                caller ! sumOfFactorInRange(lower, upper, candidate)
            }
        }

        val sum = (0 /: (0 until numberOfPartitions)) { (partialSum, i) =>
            receive {
                case sumInRange: Int => partialSum + sumInRange
            }
        }

        2 * candidate == sum
    }

    def main(args: Array[String]): Unit = {
        println(isPerfectConcurrent(6))
        println(isPerfectConcurrent(33550336))
        println(isPerfectConcurrent(33550337))
    }
}

上面代码里没有synchronized或是wait。在isPerfectConcurrent()方法里,先对这个范围内的值进行分区。在第16行,对于每个区间而言,因子部分求和的计算委托给单独的actor。当actor完成分配给它的任务,在第17行,他就会把部分和作为消息发给调用者(caller)。在这个闭包里,caller变量绑定到isPerfectConcurrent()方法里的一个变量——这个变量持有actor的引用,它是通过调用self()方法得到的,表示主线程。最后,在第22行,从委托的actor中接收消息,一次一个。用foldLeft()方法(这里显示为/😦)方法)接收了所有的部分和,以函数式风格计算了这些部分的总和。

下面对一个范围内的值查找完全数:

import scala.actors.Actor.{actor, receive, self}

object ActorTest02 {

    def sumOfFactors(number: Int) = {
        (0 /: (1 to number)) { (sum, i) => if (number % i == 0) sum + i else sum }
    }

    def isPerfect(candidate: Int) = 2 * candidate == sumOfFactors(candidate)

    def sumOfFactorInRange(lower: Int, upper: Int, number: Int) = {
        (0 /: (lower to upper)) { (sum, i) => if (number % i == 0) sum + i else sum }
    }

    def isPerfectConcurrent(candidate: Int) = {
        val RANGE = 1000000
        val numberOfPartitions = (candidate.toDouble / RANGE).ceil.toInt
        val caller = self

        for (i <- 0 until numberOfPartitions) {
            val lower = i * RANGE + 1
            val upper = candidate min (i + 1) * RANGE

            actor {
                caller ! sumOfFactorInRange(lower, upper, candidate)
            }
        }

        val sum = (0 /: (0 until numberOfPartitions)) { (partialSum, i) =>
            receive {
                case sumInRange: Int => partialSum + sumInRange
            }
        }

        2 * candidate == sum
    }

    def countPerfectNumbersInRange(start: Int, end: Int, isPerfectFinder: Int => Boolean) = {
        val startTime = System.nanoTime()
        val numberOfPerfectNumbers = (0 /: (start to end)) { (count, candidate) =>
            if (isPerfectFinder(candidate)) count + 1 else count
        }
        val endTime = System.nanoTime()

        println("Found " + numberOfPerfectNumbers +
            " perfect numbers in given range, took " +
            (endTime - startTime) / 1000000000.0 + " secs")
    }


    def main(args: Array[String]): Unit = {
        val startNumber = 33550300
        val endNumber = 33550400
        countPerfectNumbersInRange(startNumber, endNumber, isPerfect) // Found 1 perfect numbers in given range, took 40.0017878 secs
        countPerfectNumbersInRange(startNumber,endNumber,isPerfectConcurrent) // Found 1 perfect numbers in given range, took 27.4417483 secs
    }
}

在countPerfectNumberInrange()里,统计了给定范围内从start到end之间能够发现多少个完全数。实际找出候选数是否是完全数的方法委托给闭包isPerfect-Finder——它是作为参数传进来的。在给定范围内查找完全数数量所花的时间由JDK的System.nanoTime()方法计算得到。调用countPerfectNumberInrange()两次,先使用顺序实现isPerfect(),然后用并发实现isPerfectConcurrent()。
同并发实现相比,顺序计算差不多多花了1/3的时间。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值