作为其他值的容器,也都可以解引用。

deref不会阻塞。

add-watch

(add-watch reference key fn)

可以定义引用值发生改变时的回调,fn是4个参数 :key (atom/var/agent) 旧状态 新状态

(def a (atom {}))

(add-watch a :watcher
(fn [key atom old-state new-state]
(prn "-- Atom Changed --")
(prn "key" key)
(prn "atom" atom)
(prn "old-state" old-state)
(prn "new-state" new-state))) (reset! a {:foo "bar"}) ;; "-- Atom Changed --"
;; "key" :watcher
;; "atom" #<Atom@4b020acf: {:foo "bar"}>
;; "old-state" {}
;; "new-state" {:foo "bar"}
;; {:foo "bar"}

watcher在每次修改时都会被调用,但是不保证真的有改变。因此可能需要自己比较新旧值

key用于给1个ref上添加多个不同watcher,移除watcher也要用key

(remove-watch reference key)

atom

用swap! 修改

;; make an atomic list
(def players (atom ()))
;; #'user/players ;; conjoin a keyword into that list
(swap! players conj :player1)
;;=> (:player1) ;; conjoin a second keyword into the list
(swap! players conj :player2)
;;=> (:player2 :player1) ;; take a look at what is in the list
(deref players)
;;=> (:player2 :player1)

swap! 接受函数 和参数,把atom里的作为第1个参数。后面参数不限

如果f的过程复杂,比如comp 几个函数计算时间会长, 在这期间atom的值可能被修改,而如果使用atom的老值计算,但是返回前atom改成了新值,此时,老值的计算结果会被放弃。

可以强行reset!

ref与STM

软件事务内存用于避免手工管理锁;

垃圾回收用于避免手工管理内存。

——自动化的手段封装那些领域无关,繁琐的细节

Clojure 满足了ACI  而D持久不是STM关心的,因为是纯内存。

alter commute ref-set 用于修改

书上例子是

角色:

(defn character
[name & {:as opts}]
(ref (merge {:name name :items #{} :health } opts))
)

从1个角色身上移动1个装备到另一个角色身上

(defn loot
[from to]
(dosync
(when-let [item (first (:items @from))]
(alter to update-in [:items] conj item)
(alter from update-in [:items] disj item)
)
)
)

dosync表示一个事务

而when-let 如果[]里是nil 则不会执行body

disj从集合里去掉元素,返回剩余的集合

而alter,update-in  @  也都各有意义。

感觉读一块Clojure代码简直不是读代码,而是读古文。没有1个字多余,字认不齐,就1个完整句子都读不下来

alter: 记录ref在事务内第一个alter调用的值,按乐观方式提交,如果ref的值已经被别人改变,则整个事务失败。重试。

commute

可交换的计算,只关心最终结果

不会发生冲突,也不会重试,提高吞吐量,用ref最新值重算一遍

STM的事务如果耗时间太长,可能有活锁问题,总是提交失败(总有更快的任务来插队)。

write-skew 写偏差

在读、写之间 deref的其他值发生了改变。这时,把deref改成ensure,保证依赖的值被锁住,其他线程更新不了。

var

用def定义。var其实是ns级可变引用。不是“变量”

元数据修饰

私有和不可变

(def ^:private  X )
(def ^:const X )

使用前声明

(def j)
;= #'user/j
j
;= #<Unbound Unbound: #'user/j>

主要是用于表示函数的占位符 ,比如引用其他位置的的公共API。

还可以直接用宏(declare j)表示这时使用前

动态作用域

(def ^:dynamic *X*)
(binding [*X* ]
  (...)
)

习惯把这种变量两端加**,护耳。

给了函数一个隐式参数,在函数深层埋入需要改变的配置项。防止层层传递。

但坏处嘛,多少有点破坏封装的意思。

agent

每个agent维护1个action队列。

send在固定大小的线程池上执行,用于CPU密集任务

send-off不限制线程池大小,适合IO密集任务

Clojure的引用类型:var,ref,agent和atom的更多相关文章

  1. C#基础:值类型、引用类型与ref关键字

    在C#中,ref的意思是按引用传递.可以参考C++: int a = 10, b = 20; void swap(int x, int y) { int temp = x; x = y; y = te ...

  2. 《Clojure编程》笔记 第1章 进入Clojure仙境

    目录 背景简述 第1章 进入Clojure仙境 1.1 基础概念 1.2 常用的一些符号 背景简述 本人是一个自学一年Java的小菜鸡,理论上跟大多数新手的水平差不多,但我入职的新公司是要求转Cloj ...

  3. 通过一个实例重新认识引用类型,值类型,数组,堆栈,ref

    昨天在写代码时候遇到了一个问题,百思不得其解,感觉颠覆了自己对C#基础知识的认知,因为具体的情境涉及公司代码不便放出,我在这里举个例子,先上整个测试所有的代码,然后一一讲解我的思考过程: using ...

  4. 图解C#的值类型,引用类型,栈,堆,ref,out

    C# 的类型系统可分为两种类型,一是值类型,一是引用类型,这个每个C#程序员都了解.还有托管堆,栈,ref,out等等概念也是每个C#程序员都会接触到的概念,也是C#程序员面试经常考到的知识,随便搜搜 ...

  5. 值类型,引用类型,栈,堆,ref,out

    在网上收集... C#的值类型,引用类型,栈,堆,ref,out C# 的类型系统可分为两种类型,一是值类型,一是引用类型,这个每个C#程序员都了解.还有托管堆,栈,ref,out等等概念也是每个C# ...

  6. 转:图解C#的值类型,引用类型,栈,堆,ref,out

    C# 的类型系统可分为两种类型,一是值类型,一是引用类型,这个每个C#程序员都了解.还有托管堆,栈,ref,out等等概念也是每个C#程序员都会接触到的概念,也是C#程序员面试经常考到的知识,随便搜搜 ...

  7. 《Clojure编程》笔记 第4章 多线程和并发

    目录 背景简述 第4章 多线程和并发 4.0 我的问题 4.1 术语 4.1.1 一个必须要先确定的思考基础 4.2 计算在时间和空间内的转换 4.2.1 delay 4.2.2 future 4.2 ...

  8. Clojure学习笔记(一)——介绍、安装和语法

    什么是Clojure Clojure是一种动态的.强类型的.寄居在JVM上的语言. Clojure的特性: 函数式编程基础,包括一套性能可以和典型可变数据结构媲美的持久性数据结构 由JVM提供的成熟的 ...

  9. C#8.0—非空引用类型

    非空引用类型--C#8.0 原文地址:https://devblogs.microsoft.com/dotnet/try-out-nullable-reference-types/?utm_sourc ...

随机推荐

  1. Java代码 简单用于处理和数据库相关的操作

    package util; import org.apache.commons.beanutils.BeanUtils; import java.lang.reflect.InvocationTarg ...

  2. 以石子合并为例的区间DP

    区间DP,是一类具有较为固定解法的DP,一般的思路都是: first.初始化区间长度为1的情况(一般区间长度为1的较易于初始化) second. for(枚举区间长度2~n){ for(枚举左端点){ ...

  3. Collection接口的子接口——List接口

    https://docs.oracle.com/javase/8/docs/api/java/util/List.html public interface List<E> extends ...

  4. C# 面向对象4 构造函数

    构造函数 1.构造函数用来创建对象,并且可以在构造函数中对对象进行初始化. (给对象的每个属性依次的赋值) 2.构造函数是用来创建对象的特殊方法: 1.方法名和类名一样. 2.没有返回值,连void都 ...

  5. 03 python3常见内置函数

    数学相关 abs(a) : 求取绝对值.abs(-1) max(list) : 求取list最大值.max([1,2,3]) min(list) : 求取list最小值.min([1,2,3]) su ...

  6. Java---- 静态内部类与非静态内部类的区别

    静态类(只有内部类才能被声明为静态类,即静态内部类)1.只能在内部类中定义静态类 2.静态内部类与外层类绑定,即使没有创建外层类的对象,它一样存在. 3.静态类的方法可以是静态的方法也可以是非静态的方 ...

  7. 微信小程序wx.showActionSheet调用客服信息功能

    微信小程序wx.showActionSheet调用客服消息功能 官方文档的代码: wx.showActionSheet({ itemList: ['A', 'B', 'C'], success (re ...

  8. 为什么选择器:last-child有时没有起作用?

    想要有.list样式的最后一个不要下划线.为什么:last-child没有起作用? el:last-child 的匹配规则是:第一步,查找 el 选择器匹配元素的所有同级元素(siblings):第二 ...

  9. Java分布式锁三种实现方案

    方案一:数据库乐观锁 乐观锁通常实现基于数据版本(version)的记录机制实现的,比如有一张红包表(t_bonus),有一个字段(left_count)记录礼物的剩余个数,用户每领取一个奖品,对应的 ...

  10. 多线程编程-- part 9 信号量:Semaphore

    Semaphore简介 Semaphore是一个计数信号量,它的本质是一个"共享锁". 信号量维护了一个信号量许可集.线程可以通过调用acquire()来获取信号量的许可:当信号量 ...