I am quite interested in the stateful/stateless mapping and that is the easiest possible example with refs. Refs are the simplest threadsafe state primitive.
This is a counter:
(defn make-counter [] (let [counter (ref 0)] (fn [] (dosync (alter counter inc)))))
Ok, this is a bit too schemish that it should have been. I encapsulated the actual counter in the local counter variable, which is accessible only from the function I return when one makes a counter. So this counter is basically a function which always return the next number.
In scheme that would have been something like:
(define make-counter (lambda () (let ([counter 0]) (lambda () (let ([result counter]) (set! counter (+ counter 1)) result))))
The clojure variant (using dosync and alter) is thread-safe. Essentially modification to the ref is allowed only in a dosync context. Modification happens through the alter function (alter what update-function).
In order to try the code, I wrote this:
(defn print-some [get-next-function how-many] (dotimes [_ how-many] (print (.getName (Thread/currentThread))) (print ": ") (println (get-next-function)))) (let [counter (make-counter)] (dotimes [x 10] (.start (Thread. (fn [] (print-some counter 10)) (Integer/toString x)))))
I particularly like it (even though it is obviously a mistake) because of the way it messes the output. In fact the three print functions (well, one is a println) are not atomic: the output is not locked. This means that the output is completely mixed.
This is good because it shows that threads are really running in parallel (well, I have an 8 virtual core machine, after all). On the other hand, the counter is always perfect: numbers can be swapped (but that is natural) but no number is repeated twice or skipped.
No comments:
Post a Comment