`

clojure的四种并发模型(转)

 
阅读更多
转载地址:http://www.ibm.com/developerworks/cn/web/wa-clojure/index.html?ca=drs-

1、本地线程var。

注意,这里的操作需要用到绑定宏。这改变了 var 的线程本地值。
因此,在 place-offer 函数的执行范围内 droid 和 history 所指向的值将不同。
然而,在执行之外,该值没有变化。要记得,默认情况下在 Clojure 中所有事物都是不可变的(immutable)。
绑定 vars 允许在线程本地范围内对事物进行变更。如果任何其他线程想要读取该值,将看不到任何变化。
对于仅需要改变状态并将其作为执行离散任务的一部分时,这是完成任务的简单方法。如果想以其他线程可见的方式来变更状态,可能需要采用 Clojure 的 atoms。

user=> (defstruct item :title :current-price)
		#'user/item
user=> (defstruct bid :user :amount)
		#'user/bid
user=> (def history ())
		#'user/history
user=> (def droid (struct item "Droid X" 0))
		#'user/droid
user=> (defn place-offer [offer] 
  (binding [history (cons offer history) 
  droid (assoc droid :current-price (get offer :amount))] 
    (println droid history)))
		#'user/place-offer
user=> (place-offer {:user "Anthony" :amount 10})
		{:title Droid X, :current-price 10} ({:user Anthony, :amount 10})
		nil
user=>  (println droid) ;这里可以看出droid的值并没有被改变。
		{:title Droid X, :current-price 0}
		nil


2、简单同步atom

atom 是状态可变更的变量。其使用非常简单并且完全同步。换句话说,
如果调用了改变 atom 值的函数,那么当该函数结果返回时, 可以确保所有的线程都能看到新值。

 user=> (def droid (atom (struct item "Droid X" 0)))
		#'user/droid
 user=> (def history (atom ()))
		#'user/history
 user=> (defn place-offer [offer] 
			(reset! droid (assoc @droid :current-price (get offer :amount))))
		#'user/place-offer
 user=> (place-offer {:user "Anthony" :amount 10}) 
		{:title "Droid X", :current-price 10}
 user=> (println @droid) ;;这里算是另一个线程调用查看droid的状态
		{:title Droid X, :current-price 10}
		nil


3、ref的软事务处理

Clojure 的 refs 提供了其最强大的并发特色。那就是 Clojure 的 Software Transactional Memory (STM) 实现。
Refs 与 atoms 类似。与 atoms 对比,它通常只需要一行额外的代码,其主要优势是协调。
利用 refs,可在单个事务中改变多个对象的状态。该事务将具有原子性、一致性、并与 ACID 的 ACI 隔离(由于全在内存中,因此不具有持久性)。
这一隔离属性意味着任何观察者将会要么见到事务中的所有变更,要么一个也见不到。atoms 不会出现这种情况。

user=> (def droid (ref (struct item "Droid X" 0)))
		#'user/droid
 user=> (def history (ref ()))
		#'user/history
 user=> (defn place-offer [offer] 
		  (dosync
			(ref-set droid (assoc @droid :current-price (get offer :amount)))
			(ref-set history (cons offer @history))    
			))
 user=> (place-offer {:user "Tony" :amount 22})
		({:user "Tony", :amount 22})
 user=> (println @droid @history)
		{:title Droid X, :current-price 22} ({:user Tony, :amount 22})
		nil



Refs 遵循与 atoms 相同的包装模式。place-offer 函数的执行从调用 dosync 开始。此函数包装一个事务。
它提供前面所提到的协调。它允许同时改变 droid 和 history,并了解不会有任何对数据的直接读取。
如同 atoms 一样,您可以取消引用,并在执行完函数后打印该值,验证该值已经改变。

(关于函数的副作用 :函数副作用有两种原因:
1、在函数过程体内出现了改变非函数局部变量的语句,使得函数的运行能够改变全局变量的值。
2、函数体内有转出函数体的过渡语句,常见的就是GOTO语句。
  函数副作用的表现:设x是一个函数F的非局部变量,在函数体内,x值能够被改变。
  故,对于x*F来讲,不满足交换律,即x*F=F*x不一定成立(不排除特殊成立的情况),这就能够产生许多问题)
 
此时,您可能想知道 STM 在此处到底是如何工作的。如果一个线程利用数量 25 来调用 place-offer 函数,
同时,另一个线程利用数量 22 调用同一函数,将会发生什么情况?
Clojure 会确保在事务中间该值没有发生改变,因此,如果事务到达了 dosync 块的结尾,
并且,STM 可看到在当前事务启动以来有另一事务已经完成了,那么当前事务将回滚并再次运行。
这使得,仅纯函数 —— 也就是不具有副作用的函数 —— 才能作为事务的一部分,因为函数可能将被运行多次。
Clojure 采用性能非常高的永久数据结构,来确保此类事务/回滚的效率。

(同步函数:执行后等待响应再继续执行下面代码.,
异步函数:执行后不等待响应就能继续执行下面代码)
Refs 和 atoms 都是同步函数。如果不必同步变更状态,那么,agents 将提供一些优势。

4、简单异步agents

您经常需要变更状态,但是不必等待其变更,或者如果可对多个线程进行变更时,不必关心其变更的顺序。
这是个通用的模式,并且 Clojure 提供了编程模型来予以解决:agents

 user=> (def history (agent ()))
	#'user/history
 user=> (def droid (agent (struct item "Droid X" 0)))
	#'user/droid
	nil
 user=> (defn place-offer [offer]
			(send droid #(assoc % :current-price (get offer :amount))))
 user=>  (place-offer {:user "Tony" :amount 33})
	#<Agent@396477d9: {:title "Droid X", :current-price 0}>
 user=> (await droid)
	nil
 user=> (println @droid)
	{:title Droid X, :current-price 33}
	nil







分享到:
评论

相关推荐

    深入探索Clojure并发编程:从原子操作到软件事务内存

    在现代编程实践中,多核处理器的普及使得并发编程成为了提升程序性能的关键。Clojure,作为一种现代的Lisp方言...Clojure的并发模型不仅提高了程序的性能,还通过减少锁的使用和简化状态管理来提高了代码的可维护性 。

    了解java中的Clojure如何抽象并发性和共享状态

    引用是Clojure并发模型的核心,它代表了一个变量的身份,而这个身份随着时间的推移可以指向不同的不可变值。引用允许观察者(如其他函数或用户界面组件)查看其当前指向的值,而无需担心同步问题。例如,一个数据库...

    Clojure分布式状态模型Avout.zip

    Avout 为 Clojure 的内存 state 模型带来分布式应用发布,通过提供一个分布式实现的 Clojure's Multiversion Concurrency Control (MVCC) STM ,相当于分布式、持久性和可扩展的 Clojure's Atom and Ref 并发原语。...

    clojure电子书

    Clojure是一种基于Lisp的函数式编程语言,它运行在Java虚拟机(JVM)上,具有强大的并发处理能力和丰富的库支持。 首先,《Clojure Programming》是一本全面介绍Clojure语言的指南,由Chas Emerick、Crispin Cowan...

    Clojure编程乐趣]+clojure_programming.pdf

    在并发处理方面,Clojure采用软件事务内存(Software Transactional Memory, STM)机制,提供了一种声明式的处理方式,让开发者可以编写无锁并发代码,降低了并发编程的复杂性和出错概率。 Clojure的`core.async`库...

    Programming Clojure 英文电子版

    4. **Concurrency**: Clojure提供了一种更为安全和高效的并发模型——软件事务内存(STM),避免了传统锁机制所带来的问题。 5. **Java Integration**: Clojure与Java之间的调用十分直接,无需经过任何转换层,这...

    Clojure Data Analysis Cookbook

    - **并发编程**:深入探讨 Clojure 的并发模型,如原子、引用、代理和通道等。 - **社区资源和工具**:列出了一系列 Clojure 社区提供的工具和库,以及如何参与开源贡献的方法。 #### 四、本书适用人群 - **数据...

    Clojure入门教程- Clojure – Functional Programming for the JVM中文版

    - **软件事务内存(STM)**: Clojure通过STM机制实现了高度并发的编程模型,它允许开发者以事务的方式更新共享状态,从而简化了并发编程的复杂度。 #### 六、学习资源与社区 - **官方文档**: Clojure官方网站提供了...

    Clojure入门教程.pdf

    - **事务内存(STM)**:Clojure提供了一种基于软件事务的并发模型,可以简化并发编程的难度。 #### 八、社区与资源 - **社区支持**:Clojure有一个活跃的社区,可以通过邮件列表、论坛、会议等多种途径获取帮助和...

    Living clojure pdf

    4. **并发模型**:Clojure提供了强大的并发编程模型,包括原子、引用和代理等概念,这些特性使得Clojure非常适合构建高并发系统。 5. **元编程**:Clojure具有丰富的元编程能力,可以通过宏(macros)等机制自动生成...

    clojure1.4包

    3. **内存安全的并发模型**:Clojure 使用软件事务内存(Software Transactional Memory, STM)系统,使得并发编程变得简单而安全,避免了常见的线程安全问题。 4. **函数式编程**:Clojure 鼓励使用纯函数和不可变...

    Clojure编程乐趣_PDF电子书下载 带书签目录 完整版

    4. **高效的并发模型**:Clojure提供了一种称为“软件事务内存”(Software Transactional Memory, STM)的机制,可以简化并发编程的复杂度。 5. **动态类型**:Clojure是一种动态类型的语言,这意味着变量可以在...

    living clojure pdf epub azw3

    理解这些机制的工作原理,以及如何使用 `dosync` 和 `alter` 等命令来管理状态变更,是掌握Clojure并发的关键。 5. **REPL(Read-Eval-Print Loop)**:Clojure 的交互式开发环境(REPL)是其开发流程的核心部分。...

    practical clojure

    - **第5章:并发编程** - 详细介绍 Clojure 中的并发模型和工具,包括原子变量、引用、代理等。 - **第6章:面向对象编程** - 虽然 Clojure 主要是一种函数式语言,但也支持面向对象编程,本章将介绍如何在 Clojure ...

Global site tag (gtag.js) - Google Analytics