我们在上一节讨论了scalaz Future,我们说它是一个不完善的类型,最起码没有完整的异常处理机制,只能用在构建类库之类的内部环境。如果scalaz在Future类定义中增加异常处理工具的话,用户就会经常遇到Future[Throwable\/A]这样的类型,那么在进行Monadic编程时就必须使用Monad Transformer来匹配类型,程序就会变得不必要的复杂。scalaz的解决方案就是把Future[Throwable\/A]包嵌在Task类里,然后把所有Future都统一升格成Task。Task是个Monad, 这样,我们就可以统一方便地用Task来进行多线程函数式编程了。我们先看看Task的定义:scalaz.concurrent/Task.scala

class Task[+A](val get: Future[Throwable \/ A]) {

  def flatMap[B](f: A => Task[B]): Task[B] =
new Task(get flatMap {
case -\/(e) => Future.now(-\/(e))
case \/-(a) => Task.Try(f(a)) match {
case e @ -\/(_) => Future.now(e)
case \/-(task) => task.get
}
}) def map[B](f: A => B): Task[B] =
new Task(get map { _ flatMap {a => Task.Try(f(a))} })
...

Task实现了flatMap,所以是个Monad,我们可以在for-comprehension中使用Task。

Task的构建方式与Future一样:

 val tnow = Task.now { println("run now ..."); + }
//> run now ...
//| tnow : scalaz.concurrent.Task[Int] = scalaz.concurrent.Task@1a968a59
val tdelay = Task.delay { println("run delay ..."); + }
//> tdelay : scalaz.concurrent.Task[Int] = scalaz.concurrent.Task@13deb50e
val tapply = Task { println("run apply ..."); + }
//> tapply : scalaz.concurrent.Task[Int] = scalaz.concurrent.Task@59494225

同样,now函数是即时运算的。它就是一个lifter,能把一个普通运算直接升格为Task。

针对Task有几种运算方法:

 tnow.unsafePerformSync                            //> res0: Int = 7
tdelay.unsafePerformSync //> run delay ...
//| res1: Int = 7
tnow.unsafePerformAsync {
case \/-(a) => println(s"the result is: $a")
case -\/(e) => println(e.getMessage)
} //> the result is: 7
tdelay.unsafePerformAsync {
case \/-(a) => println(s"the result is: $a")
case -\/(e) => println(e.getMessage)
} //> run delay ...
//| the result is: 7
tapply.unsafePerformAsync {
case \/-(a) => println(s"the result is: $a")
case -\/(e) => println(e.getMessage)
}
Thread.sleep() //> run apply ...
//| the result is: 7

从上面的例子我们可以得出:tnow已经完成了运算,因为运算结果没有"run now ..."提示了。tdelay和tapply都是存在trampoline结构里的。但tapply存在更深一层的结构里,所以我们必须拖时间来等待tapply的运算结果。tdelay存放在Future.Suspend结构里,而tapply是存放在Future.Async结构里的,所以tdelay是一种延迟运算,而tapply就是异步运算了:

 def delay[A](a: => A): Task[A] = suspend(now(a))
def suspend[A](a: => Task[A]): Task[A] = new Task(Future.suspend(
Try(a.get) match {
case -\/(e) => Future.now(-\/(e))
case \/-(f) => f
}))
//Future.suspend:
def suspend[A](f: => Future[A]): Future[A] = Suspend(() => f) def apply[A](a: => A)(implicit pool: ExecutorService = Strategy.DefaultExecutorService): Task[A] =
new Task(Future(Try(a))(pool))
//Future.apply
def apply[A](a: => A)(implicit pool: ExecutorService = Strategy.DefaultExecutorService): Future[A] = Async { cb =>
pool.submit { new Callable[Unit] { def call = cb(a).run }}
}

好了,我们再看看Task是怎样处理异常情况的:

 def eval(value: => Int) = Task { Thread.sleep(); value }
//> eval: (value: => Int)scalaz.concurrent.Task[Int]
eval( * ).onFinish {
case None => Task { println("finished calculation successfully.") }
case Some(e) => Task { println(s"caught error [${e.getMessage}]") }
}.unsafePerformSyncAttempt match {
case -\/(e) => println(s"calculation error [${e.getMessage}]")
case \/-(a) => println(s"the result is: $a")
} //> finished calculation successfully.
//| the result is: 21
// 异常处理
eval( * / ).onFinish {
case None => Task { println("finished calculation successfully.") }
case Some(e) => Task { println(s"caught error [${e.getMessage}]") }
}.unsafePerformAsync {
case -\/(e) => println(s"calculation error [${e.getMessage}]")
case \/-(a) => println(s"the result is: $a")
}
Thread.sleep() //> caught error [/ by zero]
//| calculation error [/ by zero]

精准异常处理例子:

 import java.util.concurrent._
val timedTask = Task {Thread.sleep(); +}
//> timedTask : scalaz.concurrent.Task[Int] = scalaz.concurrent.Task@3d921e20
timedTask.timed( second).handleWith {
case e: TimeoutException => Task { println(s"calculation exceeding time limit: ${e.getMessage}") }
}.unsafePerformSync //> calculation exceeding time limit: Timed out after 1000 milliseconds
//| res2: AnyVal{def getClass(): Class[_ >: Int with Unit <: AnyVal]} = ()

再看一些多线程编程例子:

 val tasks = ( |-> ).map(n => Task{ Thread.sleep(); n })
//> tasks : List[scalaz.concurrent.Task[Int]] = List(scalaz.concurrent.Task@61
//| 8b19ad, scalaz.concurrent.Task@2d3379b4, scalaz.concurrent.Task@30c15d8b, s
//| calaz.concurrent.Task@5e0e82ae, scalaz.concurrent.Task@6771beb3)
//并行运算list of tasks
Task.gatherUnordered(tasks).unsafePerformSync //> res3: List[Int] = List(1, 2, 3, 4, 5)
val sb = new StringBuffer //> sb : StringBuffer =
val t1 = Task.fork { Thread.sleep(); sb.append("a"); Task.now("a")}
//> t1 : scalaz.concurrent.Task[String] = scalaz.concurrent.Task@6200f9cb
val t2 = Task.fork { Thread.sleep(); sb.append("b"); Task.now("b")}
//> t2 : scalaz.concurrent.Task[String] = scalaz.concurrent.Task@2002fc1d
val t3 = Task.fork { Thread.sleep(); sb.append("c"); Task.now("c")}
//> t3 : scalaz.concurrent.Task[String] = scalaz.concurrent.Task@69453e37
val t4 = Task.fork { Thread.sleep(); sb.append("d"); Task.now("d")}
//> t4 : scalaz.concurrent.Task[String] = scalaz.concurrent.Task@6f4a47c7
val t5 = Task.fork { Thread.sleep(); sb.append("e"); Task.now("e")}
//> t5 : scalaz.concurrent.Task[String] = scalaz.concurrent.Task@ae13544
val t6 = Task.fork { Thread.sleep(); sb.append("f"); Task.now("f")}
//> t6 : scalaz.concurrent.Task[String] = scalaz.concurrent.Task@3d34d211
val r = Nondeterminism[Task].nmap6(t1,t2,t3,t4,t5,t6)(List(_,_,_,_,_,_))
//> r : scalaz.concurrent.Task[List[String]] = scalaz.concurrent.Task@394df057
r.unsafePerformSync //> res4: List[String] = List(a, b, c, d, e, f)

看个耗时算法的并行运算吧:

 def seqFib(n: Int): Task[Int] =  n match {
case | => Task now
case n => {
for {
x <- seqFib(n-)
y <- seqFib(n-)
} yield x + y
}
} //> seqFib: (n: Int)scalaz.concurrent.Task[Int]
//并行算法
def parFib(n: Int): Task[Int] = n match {
case | => Task now
case n => {
val ND = Nondeterminism[Task]
for {
pair <- ND.both(parFib(n-), parFib(n-))
(x,y) = pair
} yield x + y
}
} //> parFib: (n: Int)scalaz.concurrent.Task[Int]
def msFib(n: Int, fib: Int => Task[Int]) = for {
b <- Task now { System.currentTimeMillis() }
a <- fib(n)
e <- Task now { System.currentTimeMillis() }
} yield (a, (e-b)) //> msFib: (n: Int, fib: Int => scalaz.concurrent.Task[Int])scalaz.concurrent.T
//| ask[(Int, Long)] msFib(, parFib).unsafePerformSync //> res3: (Int, Long) = (10946,373)
msFib(, seqFib).unsafePerformSync //> res4: (Int, Long) = (10946,17)

哎呀!奇怪了,为什么并行算法要慢很多呢?这个问题暂且放一放,以后再研究。当然,如果有读者能给出个解释就太感激了。
Task的线程池是如何分配的呢?看看Task.apply和Task.fork:

 /** Create a `Task` that will evaluate `a` using the given `ExecutorService`. */
def apply[A](a: => A)(implicit pool: ExecutorService = Strategy.DefaultExecutorService): Task[A] =
new Task(Future(Try(a))(pool))
def fork[A](a: => Task[A])(implicit pool: ExecutorService = Strategy.DefaultExecutorService): Task[A] =
apply(a).join
//Future.apply
/** Create a `Future` that will evaluate `a` using the given `ExecutorService`. */
def apply[A](a: => A)(implicit pool: ExecutorService = Strategy.DefaultExecutorService): Future[A] = Async { cb =>
pool.submit { new Callable[Unit] { def call = cb(a).run }}

这两个函数都包括了一个隐式参数implicit pool: ExecutorService。默认值是Strategy.DefultExecutorService。我们可以这样指定线程池:

  Task {longProcess}(myExecutorService)
Task.fork { Task {longProcess} }(myExecutorService)

下面是一个动态指定线程池的例子:

 import java.util.concurrent.{ExecutorService,Executors}
type Delegated[A] = Kleisli[Task,ExecutorService,A]
def delegate: Delegated[ExecutorService] = Kleisli(e => Task.now(e))
//> delegate: => demo.ws.task.Delegated[java.util.concurrent.ExecutorService]
implicit def delegateTaskToPool[A](ta: Task[A]): Delegated[A] = Kleisli(x => ta)
//> delegateTaskToPool: [A](ta: scalaz.concurrent.Task[A])demo.ws.task.Delegated[A]
val tPrg = for {
p <- delegate
b <- Task("x")(p)
c <- Task("y")(p)
} yield c //> tPrg : scalaz.Kleisli[scalaz.concurrent.Task,java.util.concurrent.Executor
//| Service,String] = Kleisli(<function1>)
tPrg.run(Executors.newFixedThreadPool()).unsafePerformSync
//> res3: String = y

当然,Task和scala Future之间是可以相互转换的:

 import scala.concurrent.{Future => sFuture}
import scala.util.{Success,Failure}
import scala.concurrent.ExecutionContext
def futureToTask[A](fut: sFuture[A])(implicit ec: ExecutionContext): Task[A] =
Task.async {
cb =>
fut.onComplete {
case Success(a) => cb(a.right)
case Failure(e) => cb(e.left)
}
}
def taskToFuture[A](ta: Task[A]): sFuture[A] = {
val prom = scala.concurrent.Promise[A]
ta.unsafePerformAsync {
case -\/(e) => prom.failure(e)
case \/-(a) => prom.success(a)
}
prom.future
}

与Future不同的是:Task增加了异常处理机制。

Scalaz(45)- concurrency :Task-函数式多线程编程核心配件的更多相关文章

  1. Scalaz(54)- scalaz-stream: 函数式多线程编程模式-Free Streaming Programming Model

    长久以来,函数式编程模式都被认为是一种学术研究用或教学实验用的编程模式.直到近几年由于大数据和多核CPU的兴起造成了函数式编程模式在一些实际大型应用中的出现,这才逐渐改变了人们对函数式编程无用论的观点 ...

  2. Java多线程编程核心(1)

    Java多线程编程核心(1) 停止线程 本节主要讨论如何更好停止一个线程.停止线程意味着在线程处理完成任务之前放弃当前操作. 1.停不了的线程 可能大多数同学会使用interrupt()来停止线程,但 ...

  3. Task+ConcurrentQueue多线程编程

    队列(Queue)代表了一个先进先出的对象集合.当您需要对各项进行先进先出的访问时,则使用队列.当您在列表中添加一项,称为入队,当您从列表中移除一项时,称为出队. ConcurrentQueue< ...

  4. (1)Java多线程编程核心——Java多线程技能

    1.为什么要使用多线程?多线程的优点? 提高CPU的利用率 2.什么是多线程? 3.Java实现多线程编程的两种方式? a.继承Thread类 public class MyThread01 exte ...

  5. Java多线程编程核心 - 对象及变量的并发访问

    1.什么是“线程安全”与“非线程安全”? “非线程安全”会在多个线程对同一对象总的实例变量进行并发访问时发生,产生的后果是“脏读”,也就是取到的数据其实是被更改过的. “线程安全”是以获得的实例变量的 ...

  6. Java多线程编程实战指南(核心篇)读书笔记(五)

    (尊重劳动成果,转载请注明出处:http://blog.csdn.net/qq_25827845/article/details/76730459冷血之心的博客) 博主准备恶补一番Java高并发编程相 ...

  7. Java多线程编程实战指南(核心篇)读书笔记(四)

    (尊重劳动成果,转载请注明出处:http://blog.csdn.net/qq_25827845/article/details/76690961冷血之心的博客) 博主准备恶补一番Java高并发编程相 ...

  8. 5天玩转C#并行和多线程编程 —— 第四天 Task进阶

    5天玩转C#并行和多线程编程系列文章目录 5天玩转C#并行和多线程编程 —— 第一天 认识Parallel 5天玩转C#并行和多线程编程 —— 第二天 并行集合和PLinq 5天玩转C#并行和多线程编 ...

  9. 5天玩转C#并行和多线程编程 —— 第三天 认识和使用Task

    5天玩转C#并行和多线程编程系列文章目录 5天玩转C#并行和多线程编程 —— 第一天 认识Parallel 5天玩转C#并行和多线程编程 —— 第二天 并行集合和PLinq 5天玩转C#并行和多线程编 ...

随机推荐

  1. Html5+asp.net mvc 图片压缩上传

    在做图片上传时,大图片如果没有压缩直接上传时间会非常长,因为有的图片太大,传到服务器上再压缩太慢了,而且损耗流量. 思路是将图片抽样显示在canvas上,然后用通过canvas.toDataURL方法 ...

  2. Java并发包中CyclicBarrier的工作原理、使用示例

    1. CyclicBarrier的介绍与源码分析 CyclicBarrier 的字面意思是可循环(Cyclic)使用的屏障(Barrier).它要做的事情是,让一组线程到达一个屏障(也可以叫同步点)时 ...

  3. 深入理解PHP内核(三)概览-SAPI概述

    本文链接:http://www.orlion.ml/234/ 1.在PHP生命周期的各个阶段,一些与服务相关的操作都是通过SAPI接口实现.这些内置实现的物理位置在PHP源码的SAPI目录.这个目录存 ...

  4. 用户代理字符串userAgent可实现的四个识别

    定义 用户代理字符串:navigator.userAgent HTTP规范明确规定,浏览器应该发送简短的用户代理字符串,指明浏览器的名称和版本号.但现实中却没有这么简单. 发展历史 [1]1993年美 ...

  5. App乱世,3721离我们有多远

    [总结]根据众多网友的评论,看来还是WP比较给力,IOS太贵...会对手机进行优化,安卓还行,如果给中老年人用WP比较好 声明:合理讨论,禁止骂人言论,本人也不是5毛党,发表下个人看法而已. 快过年了 ...

  6. ES6入门系列四(测试题分析)

    0.导言 ES6中新增了不少的新特性,来点测试题热热身.具体题目来源请看:http://perfectionkills.com/javascript-quiz-es6/. 以下将一题一题来解析what ...

  7. Suse碎碎念

    1. 如何查看Suse的版本号 vmpbos01:~ # lsb_release -d Description: SUSE Linux Enterprise Server 11 (x86_64) vm ...

  8. MongoDB的学习--聚合

    最近要去的新项目使用mysql,趁着还没忘记,总结记录以下MongoDB的聚合. 聚合是泛指各种可以处理批量记录并返回计算结果的操作.MongoDB提供了丰富的聚合操作,用于对数据集执行计算操作.在  ...

  9. sql server 利用发布订阅方式实现数据库同步问题

    删除本地快发布时报错: 无法作为数据库主体执行,因为主体 "dbo" 不存在.无法模拟这种类型的主体,或您没有所需的权限.已将数据库上下文更改为 'numberForcast'. ...

  10. SharedPreferences 详解(多进程,存取数组解决方案)

    一.SharedPreferences基本概念 文件保存路径:/data/data/<包名>/shared_prefs目录下目录下生成了一个SP.xml文件 SharedPreferenc ...