On Dec 22, 2:34 pm, "Mark Engelberg" <mark.engelb...@gmail.com> wrote:
> On Mon, Dec 22, 2008 at 4:23 AM, Parth Malwankar
>
> <parth.malwan...@gmail.com> wrote:
> > If I get it right, atoms are quite useful to maintain state
> > in the context of a single thread with memoization and
> > counter (within a thread) being two examples.
>
> No, RH said that atoms were definitely intended for multiple threads,
> not just single threads.  But their use is highly specific.  With
> memoization, it doesn't matter if things get retried, as long as
> things don't get "lost".  atombasically guarantees that the ref and
> the set occur atomically (via swap), so you don't have to worry about
> two threads losing something from the cache as follows:
> Current cache {:a 1 :b 2}
> One thread tries to add :c 3, and another tries to add :d 4.
> Without atomic swap, one thread could try to update the cache to {:a 1
> :b 2 :c 3} and the other to {:a 1 :b 2 :d 4} (because they are both
> basing their updates on what they see).  Whichever one wins, one of
> the values will be "lost" from the cache.)
> So atoms make this one guarantee, allowing safe multithread
> memoization, but at great risk for other types of applications,
> because most "seemingly-obvious" uses for atoms would probably be
> hosed by the possible retry.
>
> I fear a lot of people are going to end up misusing atoms.  I assume
> they were necessary to make memoization perform better than under the
> ref-with-commute approach.

It's important to distinguish between updating atoms within
transactions and outside transactions. In the former case, one has to
ensure the update function can be retried without ill-effects.
However, outside a transaction, atoms are just mutable values, that
can safely be shared between threads, provided that their updating
does not need to be coordinated with other updates (to other atoms,
refs or agents).

Here's an example; say we have a multi-threaded service where we wish
to share a single counter to use as say a serial id. The following
code initializes serid to 0 and provides a function incserid to
increment it.

(def serid (atom 0))
(defn incserid [] (swap! serid inc))
(defn docount [n cntr] (dotimes [ind n] (cntr)))

Now, the following uses a thread factory to instantiate (nthreads)
number of threads, each which will execute the above incrementor
(ncount) number of times...

(import '(java.util.concurrent Executors))
(def th-factory (Executors/newSingleThreadExecutor))
(defn do-thrds [nthreads ncount] (dotimes [t nthreads] (.submit th-
factory (partial docount ncount incserid))))

So, if we then initiate 100 threads to each increment the serid 10000
times...

(do-thrds 100 10000)
@serid
=>1000000

We see that the threads have happily shared the atom and updated it
correctly with no ill-effects. Note the lack of requirement for
dosync.

So in summary, atoms are great (and should be used in preference to
refs) where the shared state needs to be mutable, shared and
independent (not requiring coordinated update with other objects).

Regards, Adrian.
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"Clojure" group.
To post to this group, send email to clojure@googlegroups.com
To unsubscribe from this group, send email to 
clojure+unsubscr...@googlegroups.com
For more options, visit this group at 
http://groups.google.com/group/clojure?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to