Thursday, January 15, 2009

Simple Agents/Concurrent Programming in Clojure


"All concurrency issues boil down to coordinating access to mutable state. The less mutable state, the easier it is to ensure thread safety."

-- Java Concurrency in Practice (from Bill Clementson's blog)

Like Erlang, Clojure is becoming a very powerful concurrent oriented language. It is designed to be a simple language making it easier to coordinate access to resources.

"Agents provide independent, asynchronous change of individual locations. Agents are bound to a single storage location for their lifetime, and only allow mutation of that location (to a new state) to occur as a result of an action"

-- http://clojure.org/agents
Here is a simple example of two agent functions 'agent' and 'send':

;;
;; Simple example of agents (concurrent coding) in Clojure
;; The send function waits/hangs and then returns the result.
;;
;; Note: only the calls to 'Thread.sleep' in the main program
;; block the main thread.
;; @see http://clojure.org/agents
;;
;; Function definition: (agent <state>). Example, (agent "Init")
;;
;; Function definition: (send <agent> <func> & <args>).
;; Example, (send abc (fn [_] "Done"))

;; (Author: Berlin Brown (on 1/5/2009))
;;

;; (A) Define the agent as 'abc'.
(def abc
(agent "Initial Value of Agent = This string"))

;; (B) The function to send a message to the agent.
(defn go []
(send abc
(fn [_]
;; Wait here for 5 seconds)
(. java.lang.Thread sleep 5000)
(println "Print from concurrent send agent")
"1 2 3 4 (Final Value, I am done)")))

;; (C) Main entry point of the program
(defn main []
(println "Running")
;; (1) Send a message to 'abc' agent by calling 'go'
(go)
;; (2) Wait for 1 second
(. java.lang.Thread sleep 1000)
;; (3) What is the value, we shouldn't be done yet?
(println @abc)
;; (4) Wait some more, 6 more seconds
(. java.lang.Thread sleep 6000)
;; (5) Hopefully after 7 seconds, the agent is done,
;; Check the value.
(println @abc)

;; Wait a little bit more and then print done.
(. java.lang.Thread sleep 12000)
(. System exit 1)
(println "\nDone"))

(main)
;; End of Script

Output of Running Program
Running
Initial Value of Agent
Print from send agent
1 2 3 4 (Final Value, I am done)

Resources:

Clojure COP/OOP Article

http://clojure.org/agents

-----
Edited: Here is an example using a Java Thread approach (some code not included):

(defn file-monitor-loop [file]
(let [delay-t (prop-int resources-core-sys "Octane_Sys_filemonitor_delay")
enable-file-mon (prop-bool resources-win-opts "file_monitor_enabled")
pth (. file getAbsolutePath)]
(when enable-file-mon
(.start (new Thread (fn []
(while (not (. shell isDisposed))
(Thread/sleep delay-t)
(when (file-modified? file)
;; Reload the file as it grows and refresh
;; the file.
(open-file pth true)))))))))

-----

No comments: