CSP 的用法最先是 Go 语言传开来的, 看一下我从网上扒的代码:html
package main import "fmt" func ping(pings chan<- string, msg string) { pings <- msg } func pong(pings <-chan string, pongs chan<- string) { msg := <-pings pongs <- msg } func main() { pings := make(chan string, 1) pongs := make(chan string, 1) ping(pings, "passed message") pong(pings, pongs) fmt.Println(<-pongs) }
其中 <-
符号是往 channel 当中写入数据的操做.
同时注意通常 <-
的位置对于 goroutine 来讲是阻塞的,
因为 channel 可以处理异步操做, 也就是说能作到异步代码用同步写法.
更多的细节搜索 "go channel" 应该就能找到.node
除了 Go, Clojure 也实现了对于 CSP 的支持, 也就是 core.async
这个库,
在 Clojure 当中语法作了调整, 成了 >!
<!
这样的写法, 有点怪,
可是基本功能差很少, >!
和 <!
都是模仿的阻塞, channel 概念也同样:git
(let [c1 (chan) c2 (chan)] (go (while true (let [[v ch] (alts! [c1 c2])] (println "Read" v "from" ch)))) (go (>! c1 "hi")) (go (>! c2 "there")))
这个例子当中 (chan)
生成 channel, 而后用 go
生成 3 个线索...
虽然用了 while true
, 可是经过 alts!
也造成了阻塞.
更新细节搜索 "core.async" 能够找到.github
看 Wiki https://en.wikipedia.org/wiki...编程
In computer science, communicating sequential processes (CSP) is a formal language for describing patterns of interaction in concurrent systems.[1] It is a member of the family of mathematical theories of concurrency known as process algebras, or process calculi, based on message passing via channels. CSP was highly influential in the design of the occam programming language,1 and also influenced the design of programming languages such as Limbo[3] and Go.[4]浏览器
CSP 原本是用于描述并发的系统之间如何交互的, 也就是在 Go 当中的用法.
因为并发的操做一般都是异步的, 因此 CSP 也能适合异步的行为.
最主要的概念就是 Channel, 也叫作"管道", Channel 能够用于传输数据,
于是就有对于管道读和写的操做, 分别是 take!
和 put!
, Clojure 里的叫法.
前面说了, 管道看上去是阻塞代码执行的, 也就是说读和写能够进行等待.
这样就能模拟一些场景, 好比抓取网络数据再打印, 就很容易写出来了.网络
常见功能还有 alts!
, 对应 Go 的 select
, 就是多个 Channel 取首先返回的数据,
还有 merge
记不大清, 好像是汇总多个 Channel 返回的数据, 变成一个?
其余的 filter, map 等等序列的操做, 在 Channel 上也有相似实行,
另外一方面 CSP 在实用当中应该是进行了扩展, 实际功能不止这些.
好比说增长了 (timeout 1000)
这样的 Channel 等待一秒返回数据,
还有对 Channel 进行 Buffer 的功能, 以及异步的推数据等等.并发
听起来很花哨, 可是若是有动画能够展现一下就很清楚了, 我还没找到...
从总体的思路上说, 这是对于异步事件的一种抽象, 能够用来实现不少业务.
想一想办法再解释细节吧, 我这里先介绍 JavaScript 这边的状况...异步
因为 Node 6 开始有 yield
, 用同步代码写异步成为了可能,
因而有就有了 js-csp
这个模块, 经过 yield 实现了 CSP 的功能,
我还看到一个用了 async 的, 估计不能用, 可是供参考:async
https://github.com/ubolonton/...
https://github.com/dvlsg/asyn...
我直接贴一遍 README 当中的例子, 本身看看能不能看懂:
function* player(name, table) { while (true) { var ball = yield csp.take(table); // 等待取得数据 if (ball === csp.CLOSED) { // 关闭状态特殊处理 console.log(name + ": table's gone"); return; } ball.hits += 1; console.log(name + " " + ball.hits); yield csp.timeout(100); // 等待延时结束 yield csp.put(table, ball); // 推数据并等待对方取 } } csp.go(function* () { var table = csp.chan(); // 建立 Channel csp.go(player, ["ping", table]); // 至关于启动 goroutine csp.go(player, ["pong", table]); // 至关于启动 goroutine yield csp.put(table, {hits: 0}); // 推数据等待对方取 yield csp.timeout(1000); // 等待延时结束 table.close(); });
运行的效果是:
=>> node go.js ping 1 pong 2 ping 3 pong 4 ping 5 pong 6 ping 7 pong 8 ping 9 pong 10 ping: table's gone pong: table's gone
这样模拟的就是两个进程之间相互发送数据的场景.
但实际上 CSP 能够对事件流进行抽象, 也就能作出更强大的功能.
这就是在我以前推荐的这篇文章上的作的介绍, 点进去看吧:
http://jlongster.com/Taming-t...
随着浏览器和 Node 对 yield 支持的完善, 使用 js-csp 已经能够作到.
考虑到方案的灵活性, 我认为值得往深了去挖一挖.
事件流的另外一套有名的方案就是 Rx, 有 js 版本的 Rxjs.
大概来讲, Rx 是用 OOP 语法封装的 FP 风格的响应式编程方案, 操做繁多,
而 CSP 经过管道提供的是一些灵活但过于基础的原语,
看社区的讨论, 其实有很大的重叠的部分, 尽管细节还很难说...
我搜集了一些文章:
https://medium.com/@puppybits...
还有 GitHub 上的一些讨论:
https://github.com/ubolonton/...
https://github.com/cyclejs/cy...
另外还有某人用 Rx 写法模仿 CSP 方案的博客:
http://swannodette.github.io/...
http://swannodette.github.io/...
http://potetm.github.io/2014/...
http://potetm.github.io/2014/...
提及来我还没怎么消化这东西.. 可是若是看过文章里的 Demo, 你必定印象深入,流是数据和时间绑定在一块儿造成的产物, 普通的编程手法很难处理,可是 CSP 的概念让处理 Channel 中传递的数据成为了而比较灵活的事情.参考国外社区的评论, 这是具有至关大价值的一块知识, 因此在持续跟进.