You're viewing version 1.2.0 of commute. The latest stable version of Clojure Core is 1.3.0.
1.2.0 Arrow_down_16x16

commute

clojure.core

  • (commute ref fun & args)
Must be called in a transaction. Sets the in-transaction-value of
ref to:

(apply fun in-transaction-value-of-ref args)

and returns the in-transaction-value of ref.

At the commit point of the transaction, sets the value of ref to be:

(apply fun most-recently-committed-value-of-ref args)

Thus fun should be commutative, or, failing that, you must accept
last-one-in-wins behavior. commute allows for more concurrency than
ref-set.

1 Example top

  • user=> (def counter (ref 0))
    #'user/counter
    
    ;; deciding whether to increment the counter takes the terribly long time
    ;; of 100 ms -- it is decided by committee.
    user=> (defn commute-inc! [counter]
             (dosync (Thread/sleep 100) (commute counter inc)))
    #'user/commute-inc!
    user=> (defn alter-inc! [counter]
             (dosync (Thread/sleep 100) (alter counter inc)))
    #'user/alter-inc!
    
    ;; what if n people try to hit the counter at once?
    user=> (defn bombard-counter! [n f counter]
             (apply pcalls (repeat n #(f counter))))
    #'user/bombard-counter!
    
    ;; first, use alter.  Everyone is trying to update the counter, and
    ;; stepping on each other's toes, so almost every transaction is getting 
    ;; retried lots of times:
    user=> (dosync (ref-set counter 0))
    0
    user=> (time (doall (bombard-counter! 20 alter-inc! counter)))
    "Elapsed time: 2007.049224 msecs"
    (3 1 2 4 7 10 5 8 6 9 13 14 15 12 11 16 17 20 18 19)
    ;; note that it took about 2000 ms = (20 workers * 100 ms / update)
    
    ;; now, since it doesn't matter what order people update a counter in, we
    ;; use commute:
    user=> (dosync (ref-set counter 0))
    0
    user=> (time (doall (bombard-counter! 20 commute-inc! counter)))
    "Elapsed time: 401.748181 msecs"
    (1 2 3 4 5 9 10 6 7 8 11 15 13 12 14 16 19 17 18 20)
    ;; notice that we got actual concurrency this time.
Log in to add / edit an example.

See Also top

  • 0
    clojure.core/ref

    Creates and returns a Ref with an initial value of x and zero or mo

Log in to add a see also.

Plus_12x12 Minus_12x12 Source clojure/core.clj:1837 top

(defn commute
  "Must be called in a transaction. Sets the in-transaction-value of
  ref to:

  (apply fun in-transaction-value-of-ref args)

  and returns the in-transaction-value of ref.

  At the commit point of the transaction, sets the value of ref to be:

  (apply fun most-recently-committed-value-of-ref args)

  Thus fun should be commutative, or, failing that, you must accept
  last-one-in-wins behavior.  commute allows for more concurrency than
  ref-set."
  {:added "1.0"}

  [^clojure.lang.Ref ref fun & args]
    (. ref (commute fun args)))
Vars in clojure.core/commute: defn ref

Comments top

2 comment(s) for commute.

Note to understand the difference to 'alter':

Commute should be used when it doesn't matter whether anyone else has changed (altered or commuted) the ref using a successful transaction. This often implies that this ref is not affected by any conditions, i.e. it should be changed the same way no matter what. (thanks to raek for clarifying that for me)

In the concurrency video RH explains that commute is useful for actions where the order in which they alter the value doesn't matter, e.g. incrementing a counter.

Log in to add a comment.