• Clojure的引用类型:var,ref,agent和atom


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

    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 500} 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 42)
    (def ^:const  X 42)

    使用前声明

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

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

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

    动态作用域

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

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

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

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

     agent

    每个agent维护1个action队列。

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

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

  • 相关阅读:
    【设计模式学习笔记】之 装饰者模式
    【实操笔记】MySQL主从同步功能实现
    Centos6.7安装mysql 5.6简单教程
    【转载备忘】PowerDesigner16.5基本使用
    win10安装配置jdk的环境变量
    eclipse配置虚拟路径后,每次启动tomcat都会虚拟路径失效的问题解决
    Tomcat配置虚拟路径访问容器外的硬盘资源
    编写代码常用快捷键
    python爬虫之scrapy框架介绍
    python面试题(一)
  • 原文地址:https://www.cnblogs.com/xuanmanstein/p/10972875.html
Copyright © 2020-2023  润新知