SlideShare ist ein Scribd-Unternehmen logo
1 von 103
©ASERT2006-2013
Concurrency with GPars
Dr Paul King
@paulk_asert
http:/slideshare.net/paulk_asert/concurrency-gpars
https://github.com/paulk-asert/concurrency-gpars
Topics
Intro
• Useful Groovy features for Concurrency
• GPars
• Case Studies
• More Info
• Bonus Material
GPars - 2
©ASERT2006-2013
"Andy giveth and Bill taketh away"
GPars - 3
Source:HerbSutter:http://www.gotw.ca/publications/concurrency-ddj.htm
Why is it hard?
• Many issues to deal with:
– Doing things in parallel, concurrently,
asynchronously
• Processes, Threads, Co-routines, Events, Scheduling
– Sharing/Synchronization Mechanisms
• shared memory, locks, transactions, wait/notify, STM,
message passing, actors, serializability, persistence,
immutability
– Abstractions
• Shared memory on top of messaging passing
• Message passing on top of shared memory
• Dataflow, Selective Communication, Continuations
– Data Structures and Algorithms
• Queues, Heaps, Trees
• Sorting, Graph Algorithms
GPars - 4
Java Concurrency Best Practice?
• Java Concurrency in Practice:
–“If multiple threads access the
same mutable state variable
without appropriate
synchronization,
your program is broken”
–“When designing thread-safe classes,
good object-oriented techniques –
encapsulation, immutability, and clear
specification of invariants – are your
best friends”
GPars - 5
Ralph Johnson: Parallel Programming
• Styles of parallel programming
– Threads and locks
– Asynchronous messages e.g. Actors –
no or limited shared memory
– Sharing with deterministic restrictions
e.g. Fork-join
– Data parallelism
GPars - 6
©ASERT2006-2013
http://strangeloop2010.com/talk/presentation_file/14485/Johnson-DataParallelism.pdf
IncreasingAbstraction
Ralph Johnson: Parallel Programming
• Styles of parallel programming
– Threads and locks
• Nondeterministic, low-level, rumored humans can do this
– Asynchronous messages e.g. Actors –
no or limited shared memory
• Nondeterministic, ok for I/O but be careful with side-effects
– Sharing with deterministic restrictions
e.g. Fork-join
• Hopefully deterministic semantics, not designed for I/O
– Data parallelism
• Deterministic semantics, easy, efficient, not designed for I/O
GPars - 7
©ASERT2006-2013
http://strangeloop2010.com/talk/presentation_file/14485/Johnson-DataParallelism.pdf
Each approach has some caveats
GPars
• http://gpars.codehaus.org/
• Library classes and DSL sugar providing
intuitive ways for Groovy developers to
handle tasks concurrently. Logical parts:
– Data Parallelism features use JSR-166y Parallel Arrays
to enable multi-threaded collection processing
– Asynchronous functions extend the Java built-in support
for executor services to enable multi-threaded closure
processing
– Dataflow Concurrency supports natural shared-memory
concurrency model, using single-assignment variables
– Actors provide an implementation of Erlang/Scala-like
actors including "remote" actors on other machines & CSP
– Safe Agents provide a non-blocking mt-safe reference to
mutable state; inspired by "agents" in Clojure
GPars - 8
©ASERT2006-2013
Choosing approaches
GPars - 9
Formoredetailssee:http://gpars.codehaus.org/Concepts+compared
Parallel
Collections
Data Parallelism
Task
Parallelism
Streamed Data
Parallelism
Fork/
Join
Dataflow
operators
CSP
Actors
Dataflow tasks
Actors
Asynch fun’s
CSP
Fork/
Join
Immutable
Stm, Agents
Special collections
Synchronization
Linear Recursive
Linear
Recursive
Shared
Data
Irregular
Regular
Coordination approaches
GPars - 10
Source:ReGinA–GroovyinAction,2ndedition
Data Parallelism:
Fork/Join
Map/Reduce
Fixed coordination
(for collections)
Actors Explicit coordination
Safe Agents Delegated coordination
Dataflow Implicit coordination
Groovy concurrency value add
GPars - 11
JVM Concurrency
Threads, synchronization, locks, semaphores, Phaser,
executor, fork-join, concurrent collections, atomic objects
GPars
Map/reduce, fork/join,
asynchronous closures,
actors, CSP, agents,
dataflow concurrency
Your Application
Groovy
Thread & process
enhancements, immutability,
laziness, @WithReadLock,
@WithWriteLock, @Lazy,
@Synchronized
Immutable Classes
• Some Rules
– Don’t provide mutators
– Ensure that no methods can
be overridden
• Easiest to make the class final
• Or use static factories & non-public
constructors
– Make all fields final
– Make all fields private
• Avoid even public immutable constants
– Ensure exclusive access to any mutable components
• Don’t leak internal references
• Defensive copying in and out
– Optionally provide equals and hashCode methods
– Optionally provide toString method
@Immutable...
• Java Immutable Class
– As per Joshua Bloch
Effective Java
©ASERT2006-2013
public final class Person {
private final String first;
private final String last;
public String getFirst() {
return first;
}
public String getLast() {
return last;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((first == null)
? 0 : first.hashCode());
result = prime * result + ((last == null)
? 0 : last.hashCode());
return result;
}
public Person(String first, String last) {
this.first = first;
this.last = last;
}
// ...
// ...
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Person other = (Person) obj;
if (first == null) {
if (other.first != null)
return false;
} else if (!first.equals(other.first))
return false;
if (last == null) {
if (other.last != null)
return false;
} else if (!last.equals(other.last))
return false;
return true;
}
@Override
public String toString() {
return "Person(first:" + first
+ ", last:" + last + ")";
}
}
...@Immutable...
• Java Immutable Class
– As per Joshua Bloch
Effective Java
©ASERT2006-2013
public final class Person {
private final String first;
private final String last;
public String getFirst() {
return first;
}
public String getLast() {
return last;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((first == null)
? 0 : first.hashCode());
result = prime * result + ((last == null)
? 0 : last.hashCode());
return result;
}
public Person(String first, String last) {
this.first = first;
this.last = last;
}
// ...
// ...
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Person other = (Person) obj;
if (first == null) {
if (other.first != null)
return false;
} else if (!first.equals(other.first))
return false;
if (last == null) {
if (other.last != null)
return false;
} else if (!last.equals(other.last))
return false;
return true;
}
@Override
public String toString() {
return "Person(first:" + first
+ ", last:" + last + ")";
}
}
boilerplate
...@Immutable
©ASERT2006-2013
@Immutable class Person {
String first, last
}
Approaches to managing collection storage
• Mutable • Persistent
©ASERT2006-2013
• Immutable
‘c’ ‘a’ ‘c’ ‘a’
Add ‘t’ Add ‘t’ Add ‘t’
‘c’ ‘a’ ‘t’
‘c’ ‘a’
‘c’ ‘a’ ‘t’
X
‘c’
‘a’
‘t’
‘c’
‘a’
ArrayList, HashSet, HashMap asImmutable(), Guava, mop tricks fj, clj-ds, pcollections, totallylazy
Topics
• Intro
• Useful Groovy features for Concurrency
GPars
• Case Studies
• More Info
• Bonus Material
GPars - 17
©ASERT2006-2013
Choosing approaches
GPars - 18
Formoredetailssee:http://gpars.codehaus.org/Concepts+compared
Parallel
Collections
Data Parallelism
Task
Parallelism
Streamed Data
Parallelism
Fork/
Join
Dataflow
operators
CSP
Actors
Dataflow tasks
Actors
Asynch fun’s
CSP
Fork/
Join
Immutable
Stm, Agents
Special collections
Synchronization
Linear Recursive
Linear
Recursive
Shared
Data
Irregular
Regular
Groovy Sequential Collection
GPars - 19
©ASERT2006-2013
def oneStarters = (1..30)
.collect { it ** 2 }
.findAll { it ==~ '1.*' }
assert oneStarters == [1, 16, 100, 121, 144, 169, 196]
assert oneStarters.max() == 196
assert oneStarters.sum() == 747
GPars Parallel Collections…
GPars - 20
©ASERT2006-2013
import static groovyx.gpars.GParsPool.withPool
withPool {
def oneStarters = (1..30)
.collectParallel { it ** 2 }
.findAllParallel { it ==~ '1.*' }
assert oneStarters == [1, 16, 100, 121, 144, 169, 196]
assert oneStarters.maxParallel() == 196
assert oneStarters.sumParallel() == 747
}
…GPars Parallel Collections
• Suitable when
– Each iteration is independent, i.e. not:
fact[index] = index * fact[index - 1]
– Iteration logic doesn’t use non-thread safe code
– Size and indexing of iteration are important
GPars - 21
©ASERT2006-2013
import static groovyx.gpars.GParsPool.withPool
withPool {
def oneStarters = (1..30)
.collectParallel { it ** 2 }
.findAllParallel { it ==~ '1.*' }
assert oneStarters == [1, 16, 100, 121, 144, 169, 196]
assert oneStarters.maxParallel() == 196
assert oneStarters.sumParallel() == 747
}
Parallel Collection Variations
• Apply some Groovy metaprogramming
GPars - 22
©ASERT2006-2013
import static groovyx.gpars.GParsPool.withPool
withPool {
def oneStarters = (1..30).makeConcurrent()
.collect { it ** 2 }
.findAll { it ==~ '1.*' }
.findAll { it ==~ '...' }
assert oneStarters == [100, 121, 144, 169, 196]
}
import groovyx.gpars.ParallelEnhancer
def nums = 1..5
ParallelEnhancer.enhanceInstance(nums)
assert [1, 4, 9, 16, 25] == nums.collectParallel{ it * it }
GPars parallel methods for collections
Transparent Transitive? Parallel Lazy?
any { ... } anyParallel { ... } yes
collect { ... } yes collectParallel { ... }
count(filter) countParallel(filter)
each { ... } eachParallel { ... }
eachWithIndex { ... } eachWithIndexParallel { ... }
every { ... } everyParallel { ... } yes
find { ... } findParallel { ... }
findAll { ... } yes findAllParallel { ... }
findAny { ... } findAnyParallel { ... }
fold { ... } foldParallel { ... }
fold(seed) { ... } foldParallel(seed) { ... }
grep(filter) yes grepParallel(filter)
groupBy { ... } groupByParallel { ... }
max { ... } maxParallel { ... }
max() maxParallel()
min { ... } minParallel { ... }
min() minParallel()
split { ... } yes splitParallel { ... }
sum sumParallel // foldParallel +
GPars - 23Transitive means result is automatically transparent; Lazy means fails fast
FormoredetailsseeReGinAortheGParsdocumentation
GPars: Map-Reduce
GPars - 24
©ASERT2006-2013
import static groovyx.gpars.GParsPool.withPool
withPool {
def oneStarters = (1..30).parallel
.map { it ** 2 }
.filter { it ==~ '1.*' }
assert oneStarters.collection ==
[1, 16, 100, 121, 144, 169, 196]
// aggregations/reductions
assert oneStarters.max() == 196
assert oneStarters.reduce { a, b -> a + b } == 747
assert oneStarters.sum() == 747
}
GPars parallel array methods
Method Return Type
combine(initValue) { ... } Map
filter { ... } Parallel array
collection Collection
groupBy { ... } Map
map { ... } Parallel array
max() T
max { ... } T
min() T
min { ... } T
reduce { ... } T
reduce(seed) { ... } T
size() int
sort { ... } Parallel array
sum() T
parallel // on a Collection Parallel array
GPars - 25
FormoredetailsseeReGinAortheGParsdocumentation
Parallel Collections vs Map-Reduce
GPars - 26
Fork Fork
JoinJoin
Map
Map
Reduce
Map
Map
Reduce
Reduce
Map
Filter
FilterMap
Concurrency challenge…
• Suppose we have the following
calculation involving several functions:
• And we want to use our available cores …
GPars - 27
©ASERT2006-2013
// example adapted from Parallel Programming with .Net
def (f1, f2, f3, f4) = [{ sleep 1000; it }] * 3 +
[{ x, y -> x + y }]
def a = 5
def b = f1(a)
def c = f2(a)
def d = f3(c)
def f = f4(b, d)
assert f == 10
…Concurrency challenge…
• We can analyse the example’s task graph:
GPars - 28
©ASERT2006-2013
// example adapted from Parallel Programming with .Net
def (f1, f2, f3, f4) = [{ sleep 1000; it }] * 3 +
[{ x, y -> x + y }]
def a = 5
def b = f1(a)
def c = f2(a)
def d = f3(c)
def f = f4(b, d)
assert f == 10
f2
f3
f1
f4
aa
b
c
d
f
…Concurrency challenge…
• Manually using asynchronous functions:
GPars - 29
©ASERT2006-2013
// example adapted from Parallel Programming with .Net
def (f1, f2, f3, f4) = [{ sleep 1000; it }] * 3 +
[{ x, y -> x + y }]
import static groovyx.gpars.GParsPool.withPool
withPool(2) {
def a = 5
def futureB = f1.callAsync(a)
def c = f2(a)
def d = f3(c)
def f = f4(futureB.get(), d)
assert f == 10
}
f2
f3
f1
f4
aa
b
c
d
f
…Concurrency challenge
• And with GPars Dataflows:
GPars - 30
©ASERT2006-2013
def (f1, f2, f3, f4) = [{ sleep 1000; it }] * 3 +
[{ x, y -> x + y }]
import groovyx.gpars.dataflow.Dataflows
import static groovyx.gpars.dataflow.Dataflow.task
new Dataflows().with {
task { a = 5 }
task { b = f1(a) }
task { c = f2(a) }
task { d = f3(c) }
task { f = f4(b, d) }
assert f == 10
}
f2
f3
f1
f4
aa
b
c
d
f
…Concurrency challenge
• And with GPars Dataflows:
GPars - 31
©ASERT2006-2013
def (f1, f2, f3, f4) = [{ sleep 1000; it }] * 3 +
[{ x, y -> x + y }]
import groovyx.gpars.dataflow.Dataflows
import static groovyx.gpars.dataflow.Dataflow.task
new Dataflows().with {
task { f = f4(b, d) }
task { d = f3(c) }
task { c = f2(a) }
task { b = f1(a) }
task { a = 5 }
assert f == 10
}
f2
f3
f1
f4
aa
b
c
d
f
GPars: Dataflows...
GPars - 32
©ASERT2006-2013
import groovyx.gpars.dataflow.DataFlows
import static groovyx.gpars.dataflow.DataFlow.task
final flow = new DataFlows()
task { flow.result = flow.x + flow.y }
task { flow.x = 10 }
task { flow.y = 5 }
assert 15 == flow.result
new DataFlows().with {
task { result = x * y }
task { x = 10 }
task { y = 5 }
assert 50 == result
}
510
yx
*
...GPars: Dataflows...
• Evaluating:
GPars - 33
©ASERT2006-2013
import groovyx.gpars.dataflow.DataFlows
import static groovyx.gpars.dataflow.DataFlow.task
final flow = new DataFlows()
task { flow.a = 10 }
task { flow.b = 5 }
task { flow.x = flow.a - flow.b }
task { flow.y = flow.a + flow.b }
task { flow.result = flow.x * flow.y }
assert flow.result == 75
b
10 5
a
+-
*
result = (a – b) * (a + b)
x y
Question: what happens if I change the order of the task statements here?
...GPars: Dataflows...
• Naive attempt for loops
GPars - 34
©ASERT2006-2013
import groovyx.gpars.dataflow.Dataflows
import static groovyx.gpars.dataflow.Dataflow.task
final flow = new Dataflows()
[10, 20].each { thisA ->
[4, 5].each { thisB ->
task { flow.a = thisA }
task { flow.b = thisB }
task { flow.x = flow.a - flow.b }
task { flow.y = flow.a + flow.b }
task { flow.result = flow.x * flow.y }
println flow.result
}
}
// => java.lang.IllegalStateException:
A DataflowVariable can only be assigned once.
...
task { flow.a = 10 }
...
task { flow.a = 20 }
Don’t do this!
X
...GPars: Dataflows...
GPars - 35
©ASERT2006-2013
import groovyx.gpars.dataflow.DataflowStream
import static groovyx.gpars.dataflow.Dataflow.*
final streamA = new DataflowStream()
final streamB = new DataflowStream()
final streamX = new DataflowStream()
final streamY = new DataflowStream()
final results = new DataflowStream()
operator(inputs: [streamA, streamB],
outputs: [streamX, streamY]) {
a, b -> streamX << a - b; streamY << a + b
}
operator(inputs: [streamX, streamY],
outputs: [results]) { x, y -> results << x * y }
[[10, 20], [4, 5]].combinations().each{ thisA, thisB ->
task { streamA << thisA }
task { streamB << thisB }
}
4.times { println results.val }
b
10
10
20
20
4
5
4
5
a
+-
*
84
75
384
375
...GPars: Dataflows
• Suitable when:
– Your algorithms can be expressed as mutually-
independent logical tasks
• Properties:
– Inherently safe and robust (no race conditions or
livelocks)
– Amenable to static analysis
– Deadlocks “typically” become repeatable
– “Beautiful” (declarative) code
GPars - 36
©ASERT2006-2013
import groovyx.gpars.dataflow.Dataflows
import static groovyx.gpars.dataflow.Dataflow.task
final flow = new Dataflows()
task { flow.x = flow.y }
task { flow.y = flow.x }
GPars: Dataflow Sieve
GPars - 37
©ASERT2006-2013
final int requestedPrimeNumberCount = 1000
final DataflowStream initialChannel = new DataflowStream()
task {
(2..10000).each {
initialChannel << it
}
}
def filter(inChannel, int prime) {
def outChannel = new DataflowStream()
operator([inputs: [inChannel], outputs: [outChannel]]) {
if (it % prime != 0) {
bindOutput it
}
}
return outChannel
}
def currentOutput = initialChannel
requestedPrimeNumberCount.times {
int prime = currentOutput.val
println "Found: $prime"
currentOutput = filter(currentOutput, prime)
}
Source: http://groovyconsole.appspot.com/script/235002
GPars: Actors...
• Actors provide explicit coordination: they
don’t share state, instead coordinating via
asynchronous messages
– Contrasting with predefined coordination for fork/join &
map/filter/reduce & implicit coordination for dataflow
– Messages are processed one at a time normally in the
order they were sent (which is non-deterministic due to
asynchronous nature)
– Some actor systems allowing message delivery to be
prioritised; others allow for sharing some (readonly) state;
some allow remote actors for load balancing/robustness
• Not new in concept
– But has received recent publicity due to special
support in Erlang, Scala and other languages
GPars - 38
©ASERT2006-2013
…GPars: Actors...
• Class with the following lifecycle &
methods
– But also DSL sugar & enhancements
GPars - 39
©ASERT2006-2013
start()
stop()
act()
send(msg)
sendAndWait(msg)
loop { }
react { msg -> }
msg.reply(replyMsg)
receive()
join()
…GPars: Actors...
GPars - 40
©ASERT2006-2013
import groovyx.gpars.actor.DynamicDispatchActor
class VotingActor extends DynamicDispatchActor {
void onMessage(String language) {
processVote(language)
}
void onMessage(List languages) {
languages.each{ processVote it }
}
private processVote(language) {
if (language.startsWith('G')) println "You voted for $language"
else println 'Sorry, please try again'
}
}
final votes = new VotingActor().start()
votes << 'Groovy'
votes << 'C++'
votes << ['Groovy', 'Go', 'Dart']
votes.stop()
votes.join()
You voted for Groovy
Sorry, please try again
You voted for Groovy
You voted for Go
Sorry, please try again
…GPars: Actors...
GPars - 41
©ASERT2006-2013
import static groovyx.gpars.actor.Actors.*
def votes = reactor {
it.endsWith('y') ? "You voted for $it" : "Sorry, please try again"
}
println votes.sendAndWait('Groovy')
println votes.sendAndWait('JRuby')
println votes.sendAndWait('Go')
def languages = ['Groovy', 'Dart', 'C++']
def booth = actor {
languages.each{ votes << it }
loop {
languages.size().times {
react { println it }
}
stop()
}
}
booth.join(); votes.stop(); votes.join()
You voted for Groovy
You voted for JRuby
Sorry, please try again
You voted for Groovy
Sorry, please try again
Sorry, please try again
…GPars: Actors
GPars - 42
©ASERT2006-2013
import groovyx.gpars.activeobject.*
@ActiveObject
class VotingActiveObject {
@ActiveMethod vote(String language) {
processVote(language)
}
@ActiveMethod vote(List<String> languages) {
languages.collect{ processVote it }
}
private processVote(language) {
if (language.size() == 6) "You voted for $language"
else 'Sorry, please try again'
}
}
def voter = new VotingActiveObject()
def result1 = voter.vote('Scala')
def result2 = voter.vote('Groovy')
def result3 = voter.vote(['Pascal', 'Clojure', 'Groovy'])
[result1.get(), result2.get(), *result3.get()].each{ println it }
Sorry, please try again
You voted for Groovy
You voted for Pascal
Sorry, please try again
You voted for Groovy
Agents...
• Agents safeguard non-thread safe objects
• Only the agent can update the underlying
object
• “Code” to update the protected object is
sent to the agent
• Can be used with other approaches
GPars - 43
©ASERT2006-2013
…Agents…
GPars - 44
©ASERT2006-2013
def random = new Random()
def randomDelay = { sleep random.nextInt(10) }
String result = ''
('a'..'z').each { letter ->
Thread.start{
randomDelay()
result += letter
}
}
sleep 100 // poor man's join
println result
println result.size()
Unsafe!
…Agents…
GPars - 45
©ASERT2006-2013
import groovyx.gpars.agent.Agent
def random = new Random()
def randomDelay = { sleep random.nextInt(10) }
def agent = new Agent<String>('')
('a'..'z').each { letter ->
Thread.start{
randomDelay()
agent.send{ updateValue it + letter }
}
}
sleep 100 // poor man's join
String result = agent.val
println result
println result.size()
…Agents
GPars - 46
©ASERT2006-2013
import groovyx.gpars.agent.Agent
def random = new Random()
def randomDelay = { sleep random.nextInt(10) }
def agent = new Agent<String>('')
def threads = ('a'..'z').collect { letter ->
Thread.start {
randomDelay()
agent.send{ updateValue it << letter }
}
}
threads*.join()
String result = agent.val
println result
println result.size()
Software Transactional Memory…
GPars - 47
©ASERT2006-2013
@Grab('org.multiverse:multiverse-beta:0.7-RC-1')
import org.multiverse.api.references.LongRef
import static groovyx.gpars.stm.GParsStm.atomic
import static org.multiverse.api.StmUtils.newLongRef
class Account {
private final LongRef balance
Account(long initial) {
balance = newLongRef(initial)
}
void setBalance(long newBalance) {
if (newBalance < 0) throw new RuntimeException("not enough money")
balance.set newBalance
}
long getBalance() {
balance.get()
}
}
// ...
…Software Transactional Memory
GPars - 48
©ASERT2006-2013
// ...
def from = new Account(20)
def to = new Account(20)
def amount = 10
def watcher = Thread.start {
15.times {
atomic { println "from: ${from.balance}, to: ${to.balance}" }
sleep 100
}
}
sleep 150
try {
atomic {
from.balance -= amount
to.balance += amount
sleep 500
}
println 'transfer success'
} catch(all) {
println all.message
}
atomic { println "from: $from.balance, to: $to.balance" }
watcher.join()
Topics
• Intro
• Useful Groovy features for Concurrency
• GPars
Case Studies
Web Testing
Word Split
• More Info
• Bonus Material
GPars - 49
©ASERT2006-2013
GPars for testing
GPars - 50
©ASERT2006-2013
@Grab('net.sourceforge.htmlunit:htmlunit:2.6')
import com.gargoylesoftware.htmlunit.WebClient
@Grab('org.codehaus.gpars:gpars:0.10')
import static groovyx.gpars.GParsPool.*
def testCases = [
['Home', 'Bart', 'Content 1'],
['Work', 'Homer', 'Content 2'],
['Travel', 'Marge', 'Content 3'],
['Food', 'Lisa', 'Content 4']
]
withPool(3) {
testCases.eachParallel{ category, author, content ->
postAndCheck category, author, content
}
}
private postAndCheck(category, author, content) {
...
Topics
• Intro
• Useful Groovy features for Concurrency
• GPars
Case Studies
Web Testing
Word Split
• More Info
• Bonus Material
GPars - 51
©ASERT2006-2013
Word Split with Fortress
GPars - 52
©ASERT2006-2013
Guy Steele’s StrangeLoop keynote (from slide 52 onwards for several slides):
http://strangeloop2010.com/talk/presentation_file/14299/GuySteele-parallel.pdf
Word Split…
GPars - 53
©ASERT2006-2013
def swords = { s ->
def result = []
def word = ''
s.each{ ch ->
if (ch == ' ') {
if (word) result += word
word = ''
} else word += ch
}
if (word) result += word
result
}
assert swords("This is a sample") == ['This', 'is', 'a', 'sample']
assert swords("Here is a sesquipedalian string of words") ==
['Here', 'is', 'a', 'sesquipedalian', 'string', 'of', 'words']
Word Split…
GPars - 54
©ASERT2006-2013
def swords = { s ->
def result = []
def word = ''
s.each{ ch ->
if (ch == ' ') {
if (word) result += word
word = ''
} else word += ch
}
if (word) result += word
result
}
Word Split…
GPars - 55
©ASERT2006-2013
def swords = { s ->
def result = []
def word = ''
s.each{ ch ->
if (ch == ' ') {
if (word) result += word
word = ''
} else word += ch
}
if (word) result += word
result
}
…Word Split…
GPars - 56
©ASERT2006-2013
…Word Split…
GPars - 57
©ASERT2006-2013
Segment(left1, m1, right1) Segment(left2, m2, right2)
Segment(left1, m1 + [ ? ] + m2, right2)
…Word Split…
GPars - 58
©ASERT2006-2013
…Word Split…
GPars - 59
©ASERT2006-2013
class Util { static maybeWord(s) { s ? [s] : [] } }
import static Util.*
@Immutable class Chunk {
String s
public static final ZERO = new Chunk('')
def plus(Chunk other) { new Chunk(s + other.s) }
def plus(Segment other) {
new Segment(s + other.l, other.m, other.r) }
def flatten() { maybeWord(s) }
}
@Immutable class Segment {
String l; List m; String r
public static final ZERO = new Segment('', [], '')
def plus(Chunk other) { new Segment(l, m, r + other.s) }
def plus(Segment other) {
new Segment(l, m + maybeWord(r + other.l) + other.m, other.r) }
def flatten() { maybeWord(l) + m + maybeWord(r) }
}
…Word Split…
GPars - 60
©ASERT2006-2013
def processChar(ch) { ch == ' ' ?
new Segment('', [], '') :
new Chunk(ch) }
def swords(s) { s.inject(Chunk.ZERO) { result, ch ->
result + processChar(ch) } }
assert swords("Here is a sesquipedalian string of words").flatten()
== ['Here', 'is', 'a', 'sesquipedalian', 'string', 'of', 'words']
…Word Split…
GPars - 61
©ASERT2006-2013
…Word Split…
GPars - 62
©ASERT2006-2013
…Word Split…
GPars - 63
©ASERT2006-2013
THREADS = 4
def pwords(s) {
int n = (s.size() + THREADS - 1) / THREADS
def map = new ConcurrentHashMap()
(0..<THREADS).collect { i ->
Thread.start {
def (min, max) = [
[s.size(), i * n].min(), [s.size(), (i + 1) * n].min()
]
map[i] = swords(s[min..<max])
}
}*.join()
(0..<THREADS).collect { i -> map[i] }.sum().flatten()
}
…Word Split…
GPars - 64
©ASERT2006-2013
import static groovyx.gpars.GParsPool.withPool
THRESHHOLD = 10
def partition(piece) {
piece.size() <= THRESHHOLD ? piece :
[piece[0..<THRESHHOLD]] + partition(piece.substring(THRESHHOLD))
}
def pwords = { input ->
withPool(THREADS) {
partition(input).parallel.map(swords).reduce{ a, b -> a + b }.flatten()
}
}
…Guy Steele example in Groovy…
GPars - 65
©ASERT2006-2013
def words = { s ->
int n = (s.size() + THREADS - 1) / THREADS
def min = (0..<THREADS).collectEntries{ [it, [s.size(),it*n].min()] }
def max = (0..<THREADS).collectEntries{ [it, [s.size(),(it+1)*n].min()] }
def result = new DataFlows().with {
task { a = swords(s[min[0]..<max[0]]) }
task { b = swords(s[min[1]..<max[1]]) }
task { c = swords(s[min[2]..<max[2]]) }
task { d = swords(s[min[3]..<max[3]]) }
task { sum1 = a + b }
task { sum2 = c + d }
task { sum = sum1 + sum2 }
println 'Tasks ahoy!'
sum
}
switch(result) {
case Chunk: return maybeWord(result.s)
case Segment: return result.with{ maybeWord(l) + m + maybeWord(r) }
}
}
DataFlow version: partially hard-coded to 4 partitions for easier reading
…Guy Steele example in Groovy…
GPars - 66
©ASERT2006-2013
GRANULARITY_THRESHHOLD = 10
THREADS = 4
println GParsPool.withPool(THREADS) {
def result = runForkJoin(0, input.size(), input){ first, last, s ->
def size = last - first
if (size <= GRANULARITY_THRESHHOLD) {
swords(s[first..<last])
} else { // divide and conquer
def mid = first + ((last - first) >> 1)
forkOffChild(first, mid, s)
forkOffChild(mid, last, s)
childrenResults.sum()
}
}
switch(result) {
case Chunk: return maybeWord(result.s)
case Segment: return result.with{ maybeWord(l) + m + maybeWord(r) }
}
}
Fork/Join version
…Guy Steele example in Groovy
GPars - 67
©ASERT2006-2013
println GParsPool.withPool(THREADS) {
def ans = input.collectParallel{ processChar(it) }.sum()
switch(ans) {
case Chunk: return maybeWord(ans.s)
case Segment: return ans.with{ maybeWord(l) + m + maybeWord(r) }
}
}
Just leveraging the algorithm’s parallel nature
Topics
• Intro
• Useful Groovy features for Concurrency
• Gpars
• Case Studies
More Info
• Bonus Material
GPars - 68
©ASERT2006-2013
More Information about Concurrency
• Web sites
– http://gpars.codehaus.org/
– http://g.oswego.edu/
Doug Lea's home page
– http://gee.cs.oswego.edu/dl/concurrency-interest/
– http://jcip.net/
Companion site for Java Concurrency in Practice
– http://www.eecs.usma.edu/webs/people/okasaki/pubs.html#cup98
Purely Functional Data Structures
– http://delicious.com/kragen/concurrency
Concurrency bookmark list
– http://www.gotw.ca/publications/concurrency-ddj.htm
The Free Lunch is Over, Herb Sutter
– http://manticore.cs.uchicago.edu/papers/damp07.pdf
– http://mitpress.mit.edu/catalog/item/default.asp?ttype=2&tid=10142
Concepts, Techniques, and Models of Computer Programming
GPars - 69
More Information about Groovy
• Web sites
– http://groovy.codehaus.org
– http://grails.codehaus.org
– http://pleac.sourceforge.net/pleac_groovy (many examples)
– http://www.asert.com.au/training/java/GV110.htm (workshop)
• Mailing list for users
– user@groovy.codehaus.org
• Information portals
– http://www.aboutgroovy.org
– http://www.groovyblogs.org
• Documentation (1000+ pages)
– Getting Started Guide, User Guide, Developer Guide, Testing
Guide, Cookbook Examples, Advanced Usage Guide
• Books
– Several to choose from ...
GPars - 70
More Information: Groovy in Action
GPars - 71
Contains a
chapter on
GPars!
Bonus Material
• Other concurrency options
• Dining Philosopher Case Study
GPars - 72
©ASERT2006-2013
Bonus Material
• Other concurrency options
– Jetlang
– JPPF
– Multiverse
– Gruple
– Cascading
– GridGain
– ConTest
GPars - 73
©ASERT2006-2013
Lightweight threads: Jetlang
• Jetlang
– A high performance threading library
– http://code.google.com/p/jetlang/
GPars - 74
import org.jetlang.fibers.ThreadFiber
import org.jetlang.channels.MemoryRequestChannel
import org.jetlang.channels.AsyncRequest
def req = new ThreadFiber() // or pool
def reply = new ThreadFiber()
def channel = new MemoryRequestChannel()
req.start()
reply.start()
channel.subscribe(reply) { it.reply(it.request.sum()) }
AsyncRequest.withOneReply(req, channel, [3, 4, 5]) { println it }
sleep 1000
req.dispose()
reply.dispose()
12
Other High-Level Libraries: JPPF
– Open source Grid Computing platform
– http://www.jppf.org/
GPars - 75
import org.jppf.client.*
import java.util.concurrent.Callable
class Task implements Callable, Serializable {
private static final long serialVersionUID = 1162L
public Object call() {
println 'Executing Groovy'
"Hello JPPF from Groovy"
}
}
def client = new JPPFClient()
def job = new JPPFJob()
def task = new Task()
job.addTask task
def results = client.submit(job)
for (t in results) {
if (t.exception) throw t.exception
println "Result: " + t.result
}
Other High-Level Libraries: Gruple...
– http://code.google.com/p/gruple
– Simple abstraction to coordinate and synchronize
threads with ease – based on Tuplespaces
• Tuplespaces provide the illusion of a shared memory on top
of a message passing system, along with a small set of
operations to greatly simplify parallel programming
– Example Tuple:
[fname:"Vanessa", lname:"Williams", project:"Gruple"]
– Basic operations within a Tuplespace are:
• put - insert a tuple into the space
• get - read a tuple from the space (non-destructively)
• take - take a tuple from the space (a destructive read)
– Further reading: Eric Freeman, Susanne Hupfer, and
Ken Arnold. JavaSpaces Principles, Patterns, and
Practice, Addison Wesley, 1999
GPars - 76
…Other High-Level Libraries: Gruple...
GPars - 77
import org.gruple.SpaceService
def defaultSpace = SpaceService.getSpace()
defaultSpace << [fname:"Vanessa",
lname:"Williams",
project:"Gruple"]
println defaultSpace.get(fname:"Vanessa",
lname:"Williams",
project:"Gruple")
[project:Gruple, lname:Williams, fname:Vanessa]
Other High-Level Libraries: ...Gruple...
– Mandelbrot example (included in Gruple download)
GPars - 78
...
Space space = SpaceService.getSpace("mandelbrot")
Map template = createTaskTemplate()
Map task
String threadName = Thread.currentThread().name
while(true) {
ArrayList points
task = space.take(template)
println "Worker $threadName got task ${task['start']} for job ${task['jobId']
points = calculateMandelbrot(task)
Map result = createResult(task['jobId'], task['start'], points)
println "Worker $threadName writing result for task ${result['start']} for jo
space.put(result)
}
...
Other High-Level Libraries: ...Gruple
GPars - 79
Other High-Level Libraries: Cascading.groovy
– API/DSL for executing tasks on a Hadoop cluster
– http://www.cascading.org/
GPars - 80
def assembly = builder.assembly(name: "wordcount") {
eachTuple(args: ["line"], results: ["word"]) {
regexSplitGenerator(declared: ["word"], pattern: /[.,]*s+/)
}
group(["word"])
everyGroup(args: ["word"], results: ["word", "count"]) { count() }
group(["count"], reverse: true)
}
def map = builder.map() {
source(name: "wordcount") {
hfs(input) { text(["line"]) }
}
sink(name: "wordcount") {
hfs(output) { text() }
}
}
def flow = builder.flow(name: "wordcount",
map: map, assembly: assembly)
Other High-Level Libraries: GridGain…
– Simple & productive to use grid computing platform
– http://www.gridgain.com/
GPars - 81
class GridHelloWorldGroovyTask
extends GridTaskSplitAdapter<String, Integer> {
Collection split(int gridSize, Object phrase)
throws GridException {
// ...
}
Object reduce(List results) throws GridException {
// ...
}
}
import static GridFactory.*
start()
def grid = getGrid()
def future = grid.execute(GridHelloWorldGroovyTask,
"Hello World")
def phraseLen = future.get()
stop(true)
…Other High-Level Libraries: GridGain
• http://gridgain.blogspot.com/2010/10/worlds-shortest-mapreduce-
app.html
GPars - 82
words = "Counting Letters In This Phrase".split(' ')
map = new C1() { def apply(word) { word.size() } }
reduce = sumIntReducer()
println grid.forkjoin(SPREAD, yield(words, map), reduce)
// => 27
grid.forkjoin(SPREAD,yield("Counting Letters In This Phrase".split(' '),
new C1(){def apply(w){w.size()}}),sumReducer())
Testing multi-threaded applications: ConTest...
• Advanced Testing for Multi-Threaded Applications
– Tool for testing, debugging, and coverage-measuring
of concurrent programs (collects runtime statistics)
– Systematically and transparently (using a java agent)
schedules the execution of program threads in ways
likely to reveal race conditions, deadlocks, and other
intermittent bugs (collectively called synchronization
problems) with higher than normal frequency
– The ConTest run-time engine adds heuristically
controlled conditional instructions (adjustable by a
preferences file) that force thread switches, thus
helping to reveal concurrent bugs. You can use
existing tests and run ConTest multiple times – by
default different heuristics used each time it is run
• http://www.alphaworks.ibm.com/tech/contest
GPars - 83
...Testing multi-threaded applications: ConTest
GPars - 84
NUM = 5
count = 0
def incThread = { n -> Thread.start{
sleep n*10
//synchronized(ParalInc) {
count++
//}
} }
def threads = (1..NUM).collect(incThread)
threads.each{ it.join() }
assert count == NUM
targetClasses = ParalInc
timeoutTampering = true
noiseFrequency = 500
strength = 10000
Exception in thread "main" Assertion failed:
assert count == NUM
| | |
4 | 5
false
> groovyc ParalInc.groovy
> java -javaagent:../../Lib/ConTest.jar -cp %GROOVY_JAR%;. ParalInc
ParalInc.groovy
Bonus Material
• Dining Philosopher’s Case Study
– GPars Actors
– GPars CSP
– Multiverse
– Jetlang
– Gruple
GPars - 85
©ASERT2006-2013
Dining Philosophers…
GPars - 86
Philosopher
Thinking | Eating
Philosopher
Thinking | Eating
Philosopher
Thinking | Eating
Philosopher
Thinking | Eating
Philosopher
Thinking | Eating
Fork
Available | InUse
Fork
Available | InUse
Fork
Available | InUse
Fork
Available | InUse
Fork
Available | InUse
…Dining Philosophers
GPars - 87
Philosopher
Thinking | Eating
Philosopher
Thinking | Eating
Philosopher
Thinking | Eating
Philosopher
Thinking | Eating
Philosopher
Thinking | Eating
Fork
Available | InUse
Fork
Available | InUse
Fork
Available | InUse
Fork
Available | InUse
Fork
Available | InUse
Accepted | Rejected
Take | Release Take | Release
Dining Philosophers: Actors...
GPars - 88
©ASERT2006-2013
// adapted from GPars example, repo: http://git.codehaus.org/gitweb.cgi?p=gpars.git
// file: src/test/groovy/groovyx/gpars/samples/actors/DemoDiningPhilosophers.groovy
@Grab('org.codehaus.gpars:gpars:0.10')
import groovyx.gpars.actor.*
import groovy.beans.Bindable
def names = ['socrates', 'plato', 'aristotle', 'descartes', 'nietzsche']
Actors.defaultActorPGroup.resize names.size()
class Philosopher extends AbstractPooledActor {
private random = new Random()
String name
int timesEaten = 0
def forks
@Bindable String status
void act() {
assert 2 == forks.size()
loop {
think()
forks*.send new Take()
react {a ->
react {b ->
if ([a, b].any {Rejected.isCase it}) {
[a, b].find {Accepted.isCase it}?.reply new Release()
} else {
eat()
[a, b]*.reply new Release()
}
}
}
}
}
…Dining Philosophers: Actors...
GPars - 89
©ASERT2006-2013
…
void think() {
setStatus('thinking')
sleep random.nextInt(5000)
setStatus('')
}
void eat() {
setStatus("eating ${++timesEaten}")
sleep random.nextInt(3000)
setStatus('')
}
String toString() {
switch (timesEaten) {
case 0: return "$name has starved"
case 1: return "$name has eaten once"
default: return "$name has eaten $timesEaten times"
}
}
}
final class Take {}
final class Accepted {}
final class Rejected {}
final class Release {}
…Dining Philosophers: Actors...
GPars - 90
©ASERT2006-2013
…
class Fork extends AbstractPooledActor {
String name
boolean available = true
void act() {
loop {
react {message ->
switch (message) {
case Take:
if (available) {
available = false
reply new Accepted()
}
else reply new Rejected()
break
case Release:
assert !available
available = true
break
default: throw new IllegalStateException("Cannot process the message: $message")
}
}
}
}
}
def forks = (1..names.size()).collect { new Fork(name: "Fork $it") }
def philosophers = (1..names.size()).collect {
new Philosopher(name: names[it - 1], forks: [forks[it - 1], forks[it % names.size()]])
}
…Dining Philosophers: Actors
GPars - 91
©ASERT2006-2013
…
import groovy.swing.*
import java.awt.Font
import static javax.swing.JFrame.*
def frame = new SwingBuilder().frame(title: 'Philosophers', defaultCloseOperation: EXIT_ON_CLOSE) {
vbox {
hbox {
(0..<names.size()).each { i ->
def widget = textField(id: names[i], text: names[i].center(14))
widget.font = new Font(widget.font.name, widget.font.style, 36)
philosophers[i].propertyChange = { widget.text = philosophers[i].status.center(14) }
}
}
}
}
frame.pack()
frame.visible = true
forks*.start()
sleep 1000
philosophers*.start()
sleep 10000
forks*.stop()
forks*.join()
philosophers*.stop()
philosophers*.join()
frame.dispose()
philosophers.each { println it }
socrates has eaten 3 times
plato has eaten 3 times
aristotle has eaten 6 times
descartes has eaten 2 times
nietzsche has eaten 5 times
Dining Philosophers: CSP...
GPars - 92
©ASERT2006-2013
// inspired by similar examples at the web sites below:
// http://www.cs.kent.ac.uk/projects/ofa/jcsp/
// http://www.soc.napier.ac.uk/~jmk/#_Toc271192596
@Grab('org.codehaus.gpars:gpars:0.10')
import org.jcsp.lang.*
import groovyx.gpars.csp.PAR
import groovyx.gpars.csp.ALT
import static java.lang.System.currentTimeMillis
def names = ['socrates', 'plato', 'aristotle', 'descartes', 'nietzsche']
enum ForkAction { Take, Release, Stop }
import static ForkAction.*
class Philosopher implements CSProcess {
ChannelOutput leftFork, rightFork
String name
def forks = []
private random = new Random()
private timesEaten = 0
private start = currentTimeMillis()
void run() {
while (currentTimeMillis() - start < 10000) {
think()
eat()
}
[leftFork, rightFork].each { it.write(Stop) }
println toString()
}
…
…Dining Philosophers: CSP...
GPars - 93
©ASERT2006-2013
…
void think() {
println "$name is thinking"
sleep random.nextInt(50)
}
void eat() {
[leftFork, rightFork].each { it.write(Take) }
println "$name is EATING"
timesEaten++
sleep random.nextInt(200)
[leftFork, rightFork].each { it.write(Release) }
}
String toString() {
switch (timesEaten) {
case 0: return "$name has starved"
case 1: return "$name has eaten once"
default: return "$name has eaten $timesEaten times"
}
}
}
…Dining Philosophers: CSP...
GPars - 94
©ASERT2006-2013
…
class Fork implements CSProcess {
ChannelInput left, right
private active = [0, 1] as Set
void run() {
def fromPhilosopher = [left, right]
def forkAlt = new ALT(fromPhilosopher)
while (active) {
def i = forkAlt.select()
read fromPhilosopher, i, Take
read fromPhilosopher, i, Release
}
}
void read(phil, index, expected) {
if (!active.contains(index)) return
def m = phil[index].read()
if (m == Stop) active -= index
else assert m == expected
}
}
…
…Dining Philosophers: CSP
GPars - 95
©ASERT2006-2013
…
def lefts = Channel.createOne2One(names.size())
def rights = Channel.createOne2One(names.size())
def philosophers = (0..<names.size()).collect { i ->
return new Philosopher(leftFork: lefts[i].out(),
rightFork: rights[i].out(),
name: names[i])
}
def forks = (0..<names.size()).collect { i ->
return new Fork(left: lefts[i].in(),
right: rights[(i + 1) % names.size()].in())
}
def processList = philosophers + forks
new PAR(processList).run()
Why CSP?
• Amenable to proof
and analysis
GPars - 96
Picture source: http://wotug.org/parallel/theory/formal/csp/Deadlock/
Multiverse Philosophers…
GPars - 97
//@Grab('org.multiverse:multiverse-core:0.7-SNAPSHOT')
//@Grab('org.multiverse:multiverse-alpha:0.7-SNAPSHOT')
//@Grab('org.multiverse:multiverse-groovy:0.7-SNAPSHOT')
//@GrabConfig(systemClassLoader=true, initContextClassLoader = true)
// adapted multiverse Groovy example: http://git.codehaus.org/gitweb.cgi?p=multiverse.git
// file: multiverse-groovy/src/test/groovy/org/multiverse/integration/
org/multiverse/integration/examples/DiningPhilosphersTest.groovy
import org.multiverse.transactional.refs.BooleanRef
import org.multiverse.transactional.refs.IntRef
import static MultiverseGroovyLibrary.*
def food = new IntRef(5)
def names = ['socrates', 'plato', 'aristotle', 'descartes', 'nietzsche']
def forks = (1..5).collect { new Fork(id: it, free: new BooleanRef(true)) }
def philosophers = (0..4).collect {
new Philosopher(name: names[it], food: food,
left: forks[(it + 1) % 5], right: forks[it])
}
def threads = philosophers.collect { new Thread(it) }
threads*.start()
threads*.join()
philosophers.each { println it }
class Fork {
int id
BooleanRef free
void take() { free.set(false) }
void release() { free.set(true) }
}
…Multiverse Philosophers
GPars - 98
class Philosopher implements Runnable {
String name
Fork left, right
IntRef timesEaten = new IntRef()
IntRef food
void eat() {
atomic(trackreads: true, explicitRetryAllowed: true) {
left.free.await(true)
right.free.await(true)
if (food.get() > 0) {
left.take(); right.take()
timesEaten.inc(); sleep 10; food.dec()
}
}
}
void think() {
atomic(trackreads: true, explicitRetryAllowed: true) {
left.release(); right.release()
}
sleep 10
}
void run() { 10.times { eat(); think() } }
String toString() {
switch (timesEaten) {
case 0: return "$name has starved"
case 1: return "$name has eaten once"
default: return "$name has eaten $timesEaten times"
}
}
}
Jetlang Philosophers…
GPars - 99
import org.jetlang.core.Callback
import org.jetlang.fibers.ThreadFiber
import org.jetlang.channels.*
def names = ['socrates', 'plato', 'aristotle', 'descartes', 'nietzsche']
class Philosopher implements Callback {
private random = new Random()
String name
int timesEaten = 0
String status
def forks
private channels = [new MemoryRequestChannel(), new MemoryRequestChannel()]
private req = new ThreadFiber()
private reply = new ThreadFiber()
private responses = []
private gotFork = { it instanceof Accepted }
void start() {
assert forks.size() == 2
req.start()
reply.start()
(0..1).each{ channels[it].subscribe(reply, forks[it]) }
think()
}
String toString() {
switch (timesEaten) {
case 0: return "$name has starved"
case 1: return "$name has eaten once"
default: return "$name has eaten $timesEaten times"
}
}
…Jetlang Philosophers…
GPars - 100
…
void think() {
println(name + ' is thinking')
sleep random.nextInt(3000)
(0..1).each{ AsyncRequest.withOneReply(req, channels[it], new Take(it), this); }
}
void eat() {
timesEaten++
println toString()
sleep random.nextInt(2000)
}
void onMessage(Object message) {
responses << message
if (responses.size() == 2) {
if (responses.every(gotFork)) {
eat()
}
responses.findAll(gotFork).each {
int index = it.index
channels[index].publish(req, new Release(index), forks[index])
}
responses = []
think()
}
}
}
@Immutable class Take { int index }
@Immutable class Accepted { int index }
@Immutable class Rejected { int index }
@Immutable class Release { int index }
…
…Jetlang Philosophers
GPars - 101
…
class Fork implements Callback {
String name
def holder = []
void onMessage(message) {
def msg = message instanceof Request ? message.request : message
def index = msg.index
switch (msg) {
case Take:
if (!holder) {
holder << index
message.reply(new Accepted(index))
} else message.reply(new Rejected(index))
break
case Release:
assert holder == [index]
holder = []
break
default: throw new IllegalStateException("Cannot process the message: $message")
}
}
}
def forks = (1..names.size()).collect { new Fork(name: "Fork $it") }
def philosophers = (1..names.size()).collect {
new Philosopher(name: names[it - 1], forks: [forks[it - 1], forks[it % names.size()]])
}
philosophers*.start()
sleep 10000
philosophers.each { println it }
Gruple Philosophers…
GPars - 102
import org.gruple.SpaceService
import org.gruple.Space
class Philosopher {
private random = new Random()
String name
Space space
private timesEaten = 0
int id, num
boolean done = false
void run() {
while (true) {
think()
if (done) return
space.take(fork: id)
space.take(fork: (id + 1) % num)
eat()
space.put(fork: id)
space.put(fork: (id + 1) % num)
}
}
void think() {
println "$name is thinking"
sleep random.nextInt(500)
}
void eat() {
println "$name is EATING"
timesEaten++
sleep random.nextInt(1000)
}
…
…
socrates is thinking
nietzsche is thinking
descartes is EATING
aristotle is EATING
descartes is thinking
plato is EATING
aristotle is thinking
socrates is EATING
plato is thinking
nietzsche is EATING
socrates is thinking
nietzsche is thinking
descartes is EATING
descartes is thinking
socrates has eaten 5 times
plato has eaten 4 times
aristotle has eaten 4 times
descartes has eaten 4 times
nietzsche has eaten 5 times
…Gruple Philosophers
GPars - 103
…
String toString() {
switch (timesEaten) {
case 0: return "$name has starved"
case 1: return "$name has eaten once"
default: return "$name has eaten $timesEaten times"
}
}
}
def names = ['socrates', 'plato', 'aristotle', 'descartes', 'nietzsche']
def diningSpace = SpaceService.getSpace('Dining')
def philosophers = (0..<names.size()).collect{
new Philosopher(name: names[it], id: it, space: diningSpace, num: names.size())
}
(0..<names.size()).each{ diningSpace << [fork: it] }
sleep 500
def threads = (0..<names.size()).collect{ n -> Thread.start{ philosophers[n].run() } }
sleep 10000
philosophers*.done = true
sleep 2000
threads.join()
println()
philosophers.each{ println it }

Weitere ähnliche Inhalte

Was ist angesagt?

Make Testing Groovy
Make Testing GroovyMake Testing Groovy
Make Testing GroovyPaul King
 
Kotlin coroutines and spring framework
Kotlin coroutines and spring frameworkKotlin coroutines and spring framework
Kotlin coroutines and spring frameworkSunghyouk Bae
 
Kotlin @ Coupang Backend 2017
Kotlin @ Coupang Backend 2017Kotlin @ Coupang Backend 2017
Kotlin @ Coupang Backend 2017Sunghyouk Bae
 
Kotlin @ Coupang Backed - JetBrains Day seoul 2018
Kotlin @ Coupang Backed - JetBrains Day seoul 2018Kotlin @ Coupang Backed - JetBrains Day seoul 2018
Kotlin @ Coupang Backed - JetBrains Day seoul 2018Sunghyouk Bae
 
JUnit5 and TestContainers
JUnit5 and TestContainersJUnit5 and TestContainers
JUnit5 and TestContainersSunghyouk Bae
 
Scala for Java programmers
Scala for Java programmersScala for Java programmers
Scala for Java programmers輝 子安
 
Turtle Graphics in Groovy
Turtle Graphics in GroovyTurtle Graphics in Groovy
Turtle Graphics in GroovyJim Driscoll
 
Scoobi - Scala for Startups
Scoobi - Scala for StartupsScoobi - Scala for Startups
Scoobi - Scala for Startupsbmlever
 
Getting started with Elasticsearch and .NET
Getting started with Elasticsearch and .NETGetting started with Elasticsearch and .NET
Getting started with Elasticsearch and .NETTomas Jansson
 
Hello, Guava !
Hello, Guava !Hello, Guava !
Hello, Guava !輝 子安
 
The Logical Burrito - pattern matching, term rewriting and unification
The Logical Burrito - pattern matching, term rewriting and unificationThe Logical Burrito - pattern matching, term rewriting and unification
The Logical Burrito - pattern matching, term rewriting and unificationNorman Richards
 
Alternatives of JPA/Hibernate
Alternatives of JPA/HibernateAlternatives of JPA/Hibernate
Alternatives of JPA/HibernateSunghyouk Bae
 
OpenCog Developer Workshop
OpenCog Developer WorkshopOpenCog Developer Workshop
OpenCog Developer WorkshopIbby Benali
 

Was ist angesagt? (20)

Make Testing Groovy
Make Testing GroovyMake Testing Groovy
Make Testing Groovy
 
Kotlin coroutines and spring framework
Kotlin coroutines and spring frameworkKotlin coroutines and spring framework
Kotlin coroutines and spring framework
 
Kotlin @ Coupang Backend 2017
Kotlin @ Coupang Backend 2017Kotlin @ Coupang Backend 2017
Kotlin @ Coupang Backend 2017
 
Design Patterns
Design PatternsDesign Patterns
Design Patterns
 
core.logic introduction
core.logic introductioncore.logic introduction
core.logic introduction
 
Kotlin @ Coupang Backed - JetBrains Day seoul 2018
Kotlin @ Coupang Backed - JetBrains Day seoul 2018Kotlin @ Coupang Backed - JetBrains Day seoul 2018
Kotlin @ Coupang Backed - JetBrains Day seoul 2018
 
JUnit5 and TestContainers
JUnit5 and TestContainersJUnit5 and TestContainers
JUnit5 and TestContainers
 
Scala for Java programmers
Scala for Java programmersScala for Java programmers
Scala for Java programmers
 
Turtle Graphics in Groovy
Turtle Graphics in GroovyTurtle Graphics in Groovy
Turtle Graphics in Groovy
 
Scoobi - Scala for Startups
Scoobi - Scala for StartupsScoobi - Scala for Startups
Scoobi - Scala for Startups
 
Getting started with Elasticsearch and .NET
Getting started with Elasticsearch and .NETGetting started with Elasticsearch and .NET
Getting started with Elasticsearch and .NET
 
Requery overview
Requery overviewRequery overview
Requery overview
 
Hello, Guava !
Hello, Guava !Hello, Guava !
Hello, Guava !
 
Scalding for Hadoop
Scalding for HadoopScalding for Hadoop
Scalding for Hadoop
 
Groovy
GroovyGroovy
Groovy
 
Clojure And Swing
Clojure And SwingClojure And Swing
Clojure And Swing
 
The Logical Burrito - pattern matching, term rewriting and unification
The Logical Burrito - pattern matching, term rewriting and unificationThe Logical Burrito - pattern matching, term rewriting and unification
The Logical Burrito - pattern matching, term rewriting and unification
 
Poly-paradigm Java
Poly-paradigm JavaPoly-paradigm Java
Poly-paradigm Java
 
Alternatives of JPA/Hibernate
Alternatives of JPA/HibernateAlternatives of JPA/Hibernate
Alternatives of JPA/Hibernate
 
OpenCog Developer Workshop
OpenCog Developer WorkshopOpenCog Developer Workshop
OpenCog Developer Workshop
 

Andere mochten auch

groovy transforms
groovy transformsgroovy transforms
groovy transformsPaul King
 
groovy databases
groovy databasesgroovy databases
groovy databasesPaul King
 
Java Concurrency Gotchas
Java Concurrency GotchasJava Concurrency Gotchas
Java Concurrency GotchasAlex Miller
 
Lecture on Java Concurrency Day 4 on Feb 18, 2009.
Lecture on Java Concurrency Day 4 on Feb 18, 2009.Lecture on Java Concurrency Day 4 on Feb 18, 2009.
Lecture on Java Concurrency Day 4 on Feb 18, 2009.Kyung Koo Yoon
 
GPars: Groovy Parallelism for Java
GPars: Groovy Parallelism for JavaGPars: Groovy Parallelism for Java
GPars: Groovy Parallelism for JavaRussel Winder
 
Basics of Java Concurrency
Basics of Java ConcurrencyBasics of Java Concurrency
Basics of Java Concurrencykshanth2101
 
awesome groovy
awesome groovyawesome groovy
awesome groovyPaul King
 
Java concurrency
Java concurrencyJava concurrency
Java concurrencyducquoc_vn
 
groovy and concurrency
groovy and concurrencygroovy and concurrency
groovy and concurrencyPaul King
 
Groovy concurrency
Groovy concurrencyGroovy concurrency
Groovy concurrencyAlex Miller
 
Project Fortress
Project FortressProject Fortress
Project FortressAlex Miller
 
Java Course 10: Threads and Concurrency
Java Course 10: Threads and ConcurrencyJava Course 10: Threads and Concurrency
Java Course 10: Threads and ConcurrencyAnton Keks
 
Java Multithreading and Concurrency
Java Multithreading and ConcurrencyJava Multithreading and Concurrency
Java Multithreading and ConcurrencyRajesh Ananda Kumar
 
Java Concurrency in Practice
Java Concurrency in PracticeJava Concurrency in Practice
Java Concurrency in PracticeAlina Dolgikh
 
Java concurrency in practice
Java concurrency in practiceJava concurrency in practice
Java concurrency in practiceMikalai Alimenkou
 
Java Concurrency Idioms
Java Concurrency IdiomsJava Concurrency Idioms
Java Concurrency IdiomsAlex Miller
 

Andere mochten auch (16)

groovy transforms
groovy transformsgroovy transforms
groovy transforms
 
groovy databases
groovy databasesgroovy databases
groovy databases
 
Java Concurrency Gotchas
Java Concurrency GotchasJava Concurrency Gotchas
Java Concurrency Gotchas
 
Lecture on Java Concurrency Day 4 on Feb 18, 2009.
Lecture on Java Concurrency Day 4 on Feb 18, 2009.Lecture on Java Concurrency Day 4 on Feb 18, 2009.
Lecture on Java Concurrency Day 4 on Feb 18, 2009.
 
GPars: Groovy Parallelism for Java
GPars: Groovy Parallelism for JavaGPars: Groovy Parallelism for Java
GPars: Groovy Parallelism for Java
 
Basics of Java Concurrency
Basics of Java ConcurrencyBasics of Java Concurrency
Basics of Java Concurrency
 
awesome groovy
awesome groovyawesome groovy
awesome groovy
 
Java concurrency
Java concurrencyJava concurrency
Java concurrency
 
groovy and concurrency
groovy and concurrencygroovy and concurrency
groovy and concurrency
 
Groovy concurrency
Groovy concurrencyGroovy concurrency
Groovy concurrency
 
Project Fortress
Project FortressProject Fortress
Project Fortress
 
Java Course 10: Threads and Concurrency
Java Course 10: Threads and ConcurrencyJava Course 10: Threads and Concurrency
Java Course 10: Threads and Concurrency
 
Java Multithreading and Concurrency
Java Multithreading and ConcurrencyJava Multithreading and Concurrency
Java Multithreading and Concurrency
 
Java Concurrency in Practice
Java Concurrency in PracticeJava Concurrency in Practice
Java Concurrency in Practice
 
Java concurrency in practice
Java concurrency in practiceJava concurrency in practice
Java concurrency in practice
 
Java Concurrency Idioms
Java Concurrency IdiomsJava Concurrency Idioms
Java Concurrency Idioms
 

Ähnlich wie concurrency gpars

The mighty js_function
The mighty js_functionThe mighty js_function
The mighty js_functiontimotheeg
 
functional groovy
functional groovyfunctional groovy
functional groovyPaul King
 
Code level change propagation in virtual platform
Code level change propagation in virtual platformCode level change propagation in virtual platform
Code level change propagation in virtual platformHaiderAli650468
 
Getting started with ES6 : Future of javascript
Getting started with ES6 : Future of javascriptGetting started with ES6 : Future of javascript
Getting started with ES6 : Future of javascriptMohd Saeed
 
AST Transformations at JFokus
AST Transformations at JFokusAST Transformations at JFokus
AST Transformations at JFokusHamletDRC
 
Java9 Beyond Modularity - Java 9 más allá de la modularidad
Java9 Beyond Modularity - Java 9 más allá de la modularidadJava9 Beyond Modularity - Java 9 más allá de la modularidad
Java9 Beyond Modularity - Java 9 más allá de la modularidadDavid Gómez García
 
Java → kotlin: Tests Made Simple
Java → kotlin: Tests Made SimpleJava → kotlin: Tests Made Simple
Java → kotlin: Tests Made Simpleleonsabr
 
Golang basics for Java developers - Part 1
Golang basics for Java developers - Part 1Golang basics for Java developers - Part 1
Golang basics for Java developers - Part 1Robert Stern
 
Develop your next app with kotlin @ AndroidMakersFr 2017
Develop your next app with kotlin @ AndroidMakersFr 2017Develop your next app with kotlin @ AndroidMakersFr 2017
Develop your next app with kotlin @ AndroidMakersFr 2017Arnaud Giuliani
 
Groovy DSLs, from Beginner to Expert - Guillaume Laforge and Paul King - Spri...
Groovy DSLs, from Beginner to Expert - Guillaume Laforge and Paul King - Spri...Groovy DSLs, from Beginner to Expert - Guillaume Laforge and Paul King - Spri...
Groovy DSLs, from Beginner to Expert - Guillaume Laforge and Paul King - Spri...Guillaume Laforge
 
Session 37 - Intro to Workflows, API's and semantics
Session 37 - Intro to Workflows, API's and semantics Session 37 - Intro to Workflows, API's and semantics
Session 37 - Intro to Workflows, API's and semantics ISSGC Summer School
 
NET Systems Programming Learned the Hard Way.pptx
NET Systems Programming Learned the Hard Way.pptxNET Systems Programming Learned the Hard Way.pptx
NET Systems Programming Learned the Hard Way.pptxpetabridge
 
Concurrency (Fisher Syer S2GX 2010)
Concurrency (Fisher Syer S2GX 2010)Concurrency (Fisher Syer S2GX 2010)
Concurrency (Fisher Syer S2GX 2010)Dave Syer
 
Logic Equations Resolver J Script
Logic Equations Resolver   J ScriptLogic Equations Resolver   J Script
Logic Equations Resolver J ScriptRoman Agaev
 
Processing large-scale graphs with Google(TM) Pregel by MICHAEL HACKSTEIN at...
 Processing large-scale graphs with Google(TM) Pregel by MICHAEL HACKSTEIN at... Processing large-scale graphs with Google(TM) Pregel by MICHAEL HACKSTEIN at...
Processing large-scale graphs with Google(TM) Pregel by MICHAEL HACKSTEIN at...Big Data Spain
 

Ähnlich wie concurrency gpars (20)

G pars
G parsG pars
G pars
 
The mighty js_function
The mighty js_functionThe mighty js_function
The mighty js_function
 
functional groovy
functional groovyfunctional groovy
functional groovy
 
Code level change propagation in virtual platform
Code level change propagation in virtual platformCode level change propagation in virtual platform
Code level change propagation in virtual platform
 
Getting started with ES6 : Future of javascript
Getting started with ES6 : Future of javascriptGetting started with ES6 : Future of javascript
Getting started with ES6 : Future of javascript
 
AST Transformations at JFokus
AST Transformations at JFokusAST Transformations at JFokus
AST Transformations at JFokus
 
Java9 Beyond Modularity - Java 9 más allá de la modularidad
Java9 Beyond Modularity - Java 9 más allá de la modularidadJava9 Beyond Modularity - Java 9 más allá de la modularidad
Java9 Beyond Modularity - Java 9 más allá de la modularidad
 
Java → kotlin: Tests Made Simple
Java → kotlin: Tests Made SimpleJava → kotlin: Tests Made Simple
Java → kotlin: Tests Made Simple
 
Golang basics for Java developers - Part 1
Golang basics for Java developers - Part 1Golang basics for Java developers - Part 1
Golang basics for Java developers - Part 1
 
Develop your next app with kotlin @ AndroidMakersFr 2017
Develop your next app with kotlin @ AndroidMakersFr 2017Develop your next app with kotlin @ AndroidMakersFr 2017
Develop your next app with kotlin @ AndroidMakersFr 2017
 
Kpi driven-java-development-fn conf
Kpi driven-java-development-fn confKpi driven-java-development-fn conf
Kpi driven-java-development-fn conf
 
Groovy DSLs, from Beginner to Expert - Guillaume Laforge and Paul King - Spri...
Groovy DSLs, from Beginner to Expert - Guillaume Laforge and Paul King - Spri...Groovy DSLs, from Beginner to Expert - Guillaume Laforge and Paul King - Spri...
Groovy DSLs, from Beginner to Expert - Guillaume Laforge and Paul King - Spri...
 
Reason and GraphQL
Reason and GraphQLReason and GraphQL
Reason and GraphQL
 
Session 37 - Intro to Workflows, API's and semantics
Session 37 - Intro to Workflows, API's and semantics Session 37 - Intro to Workflows, API's and semantics
Session 37 - Intro to Workflows, API's and semantics
 
Java 7: Quo vadis?
Java 7: Quo vadis?Java 7: Quo vadis?
Java 7: Quo vadis?
 
NET Systems Programming Learned the Hard Way.pptx
NET Systems Programming Learned the Hard Way.pptxNET Systems Programming Learned the Hard Way.pptx
NET Systems Programming Learned the Hard Way.pptx
 
Concurrency (Fisher Syer S2GX 2010)
Concurrency (Fisher Syer S2GX 2010)Concurrency (Fisher Syer S2GX 2010)
Concurrency (Fisher Syer S2GX 2010)
 
Logic Equations Resolver J Script
Logic Equations Resolver   J ScriptLogic Equations Resolver   J Script
Logic Equations Resolver J Script
 
Processing large-scale graphs with Google(TM) Pregel by MICHAEL HACKSTEIN at...
 Processing large-scale graphs with Google(TM) Pregel by MICHAEL HACKSTEIN at... Processing large-scale graphs with Google(TM) Pregel by MICHAEL HACKSTEIN at...
Processing large-scale graphs with Google(TM) Pregel by MICHAEL HACKSTEIN at...
 
GUC Tutorial Package (9.0)
GUC Tutorial Package (9.0)GUC Tutorial Package (9.0)
GUC Tutorial Package (9.0)
 

Mehr von Paul King

Agile Testing Practices
Agile Testing PracticesAgile Testing Practices
Agile Testing PracticesPaul King
 
groovy DSLs from beginner to expert
groovy DSLs from beginner to expertgroovy DSLs from beginner to expert
groovy DSLs from beginner to expertPaul King
 
Atlassian Groovy Plugins
Atlassian Groovy PluginsAtlassian Groovy Plugins
Atlassian Groovy PluginsPaul King
 
Dynamic Language Practices
Dynamic Language PracticesDynamic Language Practices
Dynamic Language PracticesPaul King
 
Make Your Builds More Groovy
Make Your Builds More GroovyMake Your Builds More Groovy
Make Your Builds More GroovyPaul King
 
Groovy Power Features
Groovy Power FeaturesGroovy Power Features
Groovy Power FeaturesPaul King
 
Make Your Testing Groovy
Make Your Testing GroovyMake Your Testing Groovy
Make Your Testing GroovyPaul King
 
Groovy Testing Sep2009
Groovy Testing Sep2009Groovy Testing Sep2009
Groovy Testing Sep2009Paul King
 
Craig Smith & Paul King Agile Tool Hacking Taking Your Agile Development ...
Craig Smith & Paul King   Agile Tool Hacking   Taking Your Agile Development ...Craig Smith & Paul King   Agile Tool Hacking   Taking Your Agile Development ...
Craig Smith & Paul King Agile Tool Hacking Taking Your Agile Development ...Paul King
 
Groovy Tutorial
Groovy TutorialGroovy Tutorial
Groovy TutorialPaul King
 
XML and Web Services with Groovy
XML and Web Services with GroovyXML and Web Services with Groovy
XML and Web Services with GroovyPaul King
 

Mehr von Paul King (12)

Agile Testing Practices
Agile Testing PracticesAgile Testing Practices
Agile Testing Practices
 
groovy DSLs from beginner to expert
groovy DSLs from beginner to expertgroovy DSLs from beginner to expert
groovy DSLs from beginner to expert
 
GroovyDSLs
GroovyDSLsGroovyDSLs
GroovyDSLs
 
Atlassian Groovy Plugins
Atlassian Groovy PluginsAtlassian Groovy Plugins
Atlassian Groovy Plugins
 
Dynamic Language Practices
Dynamic Language PracticesDynamic Language Practices
Dynamic Language Practices
 
Make Your Builds More Groovy
Make Your Builds More GroovyMake Your Builds More Groovy
Make Your Builds More Groovy
 
Groovy Power Features
Groovy Power FeaturesGroovy Power Features
Groovy Power Features
 
Make Your Testing Groovy
Make Your Testing GroovyMake Your Testing Groovy
Make Your Testing Groovy
 
Groovy Testing Sep2009
Groovy Testing Sep2009Groovy Testing Sep2009
Groovy Testing Sep2009
 
Craig Smith & Paul King Agile Tool Hacking Taking Your Agile Development ...
Craig Smith & Paul King   Agile Tool Hacking   Taking Your Agile Development ...Craig Smith & Paul King   Agile Tool Hacking   Taking Your Agile Development ...
Craig Smith & Paul King Agile Tool Hacking Taking Your Agile Development ...
 
Groovy Tutorial
Groovy TutorialGroovy Tutorial
Groovy Tutorial
 
XML and Web Services with Groovy
XML and Web Services with GroovyXML and Web Services with Groovy
XML and Web Services with Groovy
 

Kürzlich hochgeladen

#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024BookNet Canada
 
Handwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed textsHandwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed textsMaria Levchenko
 
Maximizing Board Effectiveness 2024 Webinar.pptx
Maximizing Board Effectiveness 2024 Webinar.pptxMaximizing Board Effectiveness 2024 Webinar.pptx
Maximizing Board Effectiveness 2024 Webinar.pptxOnBoard
 
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptxHampshireHUG
 
Kalyanpur ) Call Girls in Lucknow Finest Escorts Service 🍸 8923113531 🎰 Avail...
Kalyanpur ) Call Girls in Lucknow Finest Escorts Service 🍸 8923113531 🎰 Avail...Kalyanpur ) Call Girls in Lucknow Finest Escorts Service 🍸 8923113531 🎰 Avail...
Kalyanpur ) Call Girls in Lucknow Finest Escorts Service 🍸 8923113531 🎰 Avail...gurkirankumar98700
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerThousandEyes
 
Injustice - Developers Among Us (SciFiDevCon 2024)
Injustice - Developers Among Us (SciFiDevCon 2024)Injustice - Developers Among Us (SciFiDevCon 2024)
Injustice - Developers Among Us (SciFiDevCon 2024)Allon Mureinik
 
08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking Men08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking MenDelhi Call girls
 
Scaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationScaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationRadu Cotescu
 
Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...
Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...
Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...shyamraj55
 
Understanding the Laravel MVC Architecture
Understanding the Laravel MVC ArchitectureUnderstanding the Laravel MVC Architecture
Understanding the Laravel MVC ArchitecturePixlogix Infotech
 
My Hashitalk Indonesia April 2024 Presentation
My Hashitalk Indonesia April 2024 PresentationMy Hashitalk Indonesia April 2024 Presentation
My Hashitalk Indonesia April 2024 PresentationRidwan Fadjar
 
CNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of ServiceCNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of Servicegiselly40
 
Histor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slideHistor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slidevu2urc
 
Enhancing Worker Digital Experience: A Hands-on Workshop for Partners
Enhancing Worker Digital Experience: A Hands-on Workshop for PartnersEnhancing Worker Digital Experience: A Hands-on Workshop for Partners
Enhancing Worker Digital Experience: A Hands-on Workshop for PartnersThousandEyes
 
Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024The Digital Insurer
 
Salesforce Community Group Quito, Salesforce 101
Salesforce Community Group Quito, Salesforce 101Salesforce Community Group Quito, Salesforce 101
Salesforce Community Group Quito, Salesforce 101Paola De la Torre
 
The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024Rafal Los
 
Swan(sea) Song – personal research during my six years at Swansea ... and bey...
Swan(sea) Song – personal research during my six years at Swansea ... and bey...Swan(sea) Song – personal research during my six years at Swansea ... and bey...
Swan(sea) Song – personal research during my six years at Swansea ... and bey...Alan Dix
 
WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure service
WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure serviceWhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure service
WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure servicePooja Nehwal
 

Kürzlich hochgeladen (20)

#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
 
Handwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed textsHandwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed texts
 
Maximizing Board Effectiveness 2024 Webinar.pptx
Maximizing Board Effectiveness 2024 Webinar.pptxMaximizing Board Effectiveness 2024 Webinar.pptx
Maximizing Board Effectiveness 2024 Webinar.pptx
 
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
 
Kalyanpur ) Call Girls in Lucknow Finest Escorts Service 🍸 8923113531 🎰 Avail...
Kalyanpur ) Call Girls in Lucknow Finest Escorts Service 🍸 8923113531 🎰 Avail...Kalyanpur ) Call Girls in Lucknow Finest Escorts Service 🍸 8923113531 🎰 Avail...
Kalyanpur ) Call Girls in Lucknow Finest Escorts Service 🍸 8923113531 🎰 Avail...
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 
Injustice - Developers Among Us (SciFiDevCon 2024)
Injustice - Developers Among Us (SciFiDevCon 2024)Injustice - Developers Among Us (SciFiDevCon 2024)
Injustice - Developers Among Us (SciFiDevCon 2024)
 
08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking Men08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking Men
 
Scaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationScaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organization
 
Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...
Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...
Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...
 
Understanding the Laravel MVC Architecture
Understanding the Laravel MVC ArchitectureUnderstanding the Laravel MVC Architecture
Understanding the Laravel MVC Architecture
 
My Hashitalk Indonesia April 2024 Presentation
My Hashitalk Indonesia April 2024 PresentationMy Hashitalk Indonesia April 2024 Presentation
My Hashitalk Indonesia April 2024 Presentation
 
CNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of ServiceCNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of Service
 
Histor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slideHistor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slide
 
Enhancing Worker Digital Experience: A Hands-on Workshop for Partners
Enhancing Worker Digital Experience: A Hands-on Workshop for PartnersEnhancing Worker Digital Experience: A Hands-on Workshop for Partners
Enhancing Worker Digital Experience: A Hands-on Workshop for Partners
 
Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024
 
Salesforce Community Group Quito, Salesforce 101
Salesforce Community Group Quito, Salesforce 101Salesforce Community Group Quito, Salesforce 101
Salesforce Community Group Quito, Salesforce 101
 
The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024
 
Swan(sea) Song – personal research during my six years at Swansea ... and bey...
Swan(sea) Song – personal research during my six years at Swansea ... and bey...Swan(sea) Song – personal research during my six years at Swansea ... and bey...
Swan(sea) Song – personal research during my six years at Swansea ... and bey...
 
WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure service
WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure serviceWhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure service
WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure service
 

concurrency gpars

  • 1. ©ASERT2006-2013 Concurrency with GPars Dr Paul King @paulk_asert http:/slideshare.net/paulk_asert/concurrency-gpars https://github.com/paulk-asert/concurrency-gpars
  • 2. Topics Intro • Useful Groovy features for Concurrency • GPars • Case Studies • More Info • Bonus Material GPars - 2 ©ASERT2006-2013
  • 3. "Andy giveth and Bill taketh away" GPars - 3 Source:HerbSutter:http://www.gotw.ca/publications/concurrency-ddj.htm
  • 4. Why is it hard? • Many issues to deal with: – Doing things in parallel, concurrently, asynchronously • Processes, Threads, Co-routines, Events, Scheduling – Sharing/Synchronization Mechanisms • shared memory, locks, transactions, wait/notify, STM, message passing, actors, serializability, persistence, immutability – Abstractions • Shared memory on top of messaging passing • Message passing on top of shared memory • Dataflow, Selective Communication, Continuations – Data Structures and Algorithms • Queues, Heaps, Trees • Sorting, Graph Algorithms GPars - 4
  • 5. Java Concurrency Best Practice? • Java Concurrency in Practice: –“If multiple threads access the same mutable state variable without appropriate synchronization, your program is broken” –“When designing thread-safe classes, good object-oriented techniques – encapsulation, immutability, and clear specification of invariants – are your best friends” GPars - 5
  • 6. Ralph Johnson: Parallel Programming • Styles of parallel programming – Threads and locks – Asynchronous messages e.g. Actors – no or limited shared memory – Sharing with deterministic restrictions e.g. Fork-join – Data parallelism GPars - 6 ©ASERT2006-2013 http://strangeloop2010.com/talk/presentation_file/14485/Johnson-DataParallelism.pdf IncreasingAbstraction
  • 7. Ralph Johnson: Parallel Programming • Styles of parallel programming – Threads and locks • Nondeterministic, low-level, rumored humans can do this – Asynchronous messages e.g. Actors – no or limited shared memory • Nondeterministic, ok for I/O but be careful with side-effects – Sharing with deterministic restrictions e.g. Fork-join • Hopefully deterministic semantics, not designed for I/O – Data parallelism • Deterministic semantics, easy, efficient, not designed for I/O GPars - 7 ©ASERT2006-2013 http://strangeloop2010.com/talk/presentation_file/14485/Johnson-DataParallelism.pdf Each approach has some caveats
  • 8. GPars • http://gpars.codehaus.org/ • Library classes and DSL sugar providing intuitive ways for Groovy developers to handle tasks concurrently. Logical parts: – Data Parallelism features use JSR-166y Parallel Arrays to enable multi-threaded collection processing – Asynchronous functions extend the Java built-in support for executor services to enable multi-threaded closure processing – Dataflow Concurrency supports natural shared-memory concurrency model, using single-assignment variables – Actors provide an implementation of Erlang/Scala-like actors including "remote" actors on other machines & CSP – Safe Agents provide a non-blocking mt-safe reference to mutable state; inspired by "agents" in Clojure GPars - 8 ©ASERT2006-2013
  • 9. Choosing approaches GPars - 9 Formoredetailssee:http://gpars.codehaus.org/Concepts+compared Parallel Collections Data Parallelism Task Parallelism Streamed Data Parallelism Fork/ Join Dataflow operators CSP Actors Dataflow tasks Actors Asynch fun’s CSP Fork/ Join Immutable Stm, Agents Special collections Synchronization Linear Recursive Linear Recursive Shared Data Irregular Regular
  • 10. Coordination approaches GPars - 10 Source:ReGinA–GroovyinAction,2ndedition Data Parallelism: Fork/Join Map/Reduce Fixed coordination (for collections) Actors Explicit coordination Safe Agents Delegated coordination Dataflow Implicit coordination
  • 11. Groovy concurrency value add GPars - 11 JVM Concurrency Threads, synchronization, locks, semaphores, Phaser, executor, fork-join, concurrent collections, atomic objects GPars Map/reduce, fork/join, asynchronous closures, actors, CSP, agents, dataflow concurrency Your Application Groovy Thread & process enhancements, immutability, laziness, @WithReadLock, @WithWriteLock, @Lazy, @Synchronized
  • 12. Immutable Classes • Some Rules – Don’t provide mutators – Ensure that no methods can be overridden • Easiest to make the class final • Or use static factories & non-public constructors – Make all fields final – Make all fields private • Avoid even public immutable constants – Ensure exclusive access to any mutable components • Don’t leak internal references • Defensive copying in and out – Optionally provide equals and hashCode methods – Optionally provide toString method
  • 13. @Immutable... • Java Immutable Class – As per Joshua Bloch Effective Java ©ASERT2006-2013 public final class Person { private final String first; private final String last; public String getFirst() { return first; } public String getLast() { return last; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((first == null) ? 0 : first.hashCode()); result = prime * result + ((last == null) ? 0 : last.hashCode()); return result; } public Person(String first, String last) { this.first = first; this.last = last; } // ... // ... @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Person other = (Person) obj; if (first == null) { if (other.first != null) return false; } else if (!first.equals(other.first)) return false; if (last == null) { if (other.last != null) return false; } else if (!last.equals(other.last)) return false; return true; } @Override public String toString() { return "Person(first:" + first + ", last:" + last + ")"; } }
  • 14. ...@Immutable... • Java Immutable Class – As per Joshua Bloch Effective Java ©ASERT2006-2013 public final class Person { private final String first; private final String last; public String getFirst() { return first; } public String getLast() { return last; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((first == null) ? 0 : first.hashCode()); result = prime * result + ((last == null) ? 0 : last.hashCode()); return result; } public Person(String first, String last) { this.first = first; this.last = last; } // ... // ... @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Person other = (Person) obj; if (first == null) { if (other.first != null) return false; } else if (!first.equals(other.first)) return false; if (last == null) { if (other.last != null) return false; } else if (!last.equals(other.last)) return false; return true; } @Override public String toString() { return "Person(first:" + first + ", last:" + last + ")"; } } boilerplate
  • 16. Approaches to managing collection storage • Mutable • Persistent ©ASERT2006-2013 • Immutable ‘c’ ‘a’ ‘c’ ‘a’ Add ‘t’ Add ‘t’ Add ‘t’ ‘c’ ‘a’ ‘t’ ‘c’ ‘a’ ‘c’ ‘a’ ‘t’ X ‘c’ ‘a’ ‘t’ ‘c’ ‘a’ ArrayList, HashSet, HashMap asImmutable(), Guava, mop tricks fj, clj-ds, pcollections, totallylazy
  • 17. Topics • Intro • Useful Groovy features for Concurrency GPars • Case Studies • More Info • Bonus Material GPars - 17 ©ASERT2006-2013
  • 18. Choosing approaches GPars - 18 Formoredetailssee:http://gpars.codehaus.org/Concepts+compared Parallel Collections Data Parallelism Task Parallelism Streamed Data Parallelism Fork/ Join Dataflow operators CSP Actors Dataflow tasks Actors Asynch fun’s CSP Fork/ Join Immutable Stm, Agents Special collections Synchronization Linear Recursive Linear Recursive Shared Data Irregular Regular
  • 19. Groovy Sequential Collection GPars - 19 ©ASERT2006-2013 def oneStarters = (1..30) .collect { it ** 2 } .findAll { it ==~ '1.*' } assert oneStarters == [1, 16, 100, 121, 144, 169, 196] assert oneStarters.max() == 196 assert oneStarters.sum() == 747
  • 20. GPars Parallel Collections… GPars - 20 ©ASERT2006-2013 import static groovyx.gpars.GParsPool.withPool withPool { def oneStarters = (1..30) .collectParallel { it ** 2 } .findAllParallel { it ==~ '1.*' } assert oneStarters == [1, 16, 100, 121, 144, 169, 196] assert oneStarters.maxParallel() == 196 assert oneStarters.sumParallel() == 747 }
  • 21. …GPars Parallel Collections • Suitable when – Each iteration is independent, i.e. not: fact[index] = index * fact[index - 1] – Iteration logic doesn’t use non-thread safe code – Size and indexing of iteration are important GPars - 21 ©ASERT2006-2013 import static groovyx.gpars.GParsPool.withPool withPool { def oneStarters = (1..30) .collectParallel { it ** 2 } .findAllParallel { it ==~ '1.*' } assert oneStarters == [1, 16, 100, 121, 144, 169, 196] assert oneStarters.maxParallel() == 196 assert oneStarters.sumParallel() == 747 }
  • 22. Parallel Collection Variations • Apply some Groovy metaprogramming GPars - 22 ©ASERT2006-2013 import static groovyx.gpars.GParsPool.withPool withPool { def oneStarters = (1..30).makeConcurrent() .collect { it ** 2 } .findAll { it ==~ '1.*' } .findAll { it ==~ '...' } assert oneStarters == [100, 121, 144, 169, 196] } import groovyx.gpars.ParallelEnhancer def nums = 1..5 ParallelEnhancer.enhanceInstance(nums) assert [1, 4, 9, 16, 25] == nums.collectParallel{ it * it }
  • 23. GPars parallel methods for collections Transparent Transitive? Parallel Lazy? any { ... } anyParallel { ... } yes collect { ... } yes collectParallel { ... } count(filter) countParallel(filter) each { ... } eachParallel { ... } eachWithIndex { ... } eachWithIndexParallel { ... } every { ... } everyParallel { ... } yes find { ... } findParallel { ... } findAll { ... } yes findAllParallel { ... } findAny { ... } findAnyParallel { ... } fold { ... } foldParallel { ... } fold(seed) { ... } foldParallel(seed) { ... } grep(filter) yes grepParallel(filter) groupBy { ... } groupByParallel { ... } max { ... } maxParallel { ... } max() maxParallel() min { ... } minParallel { ... } min() minParallel() split { ... } yes splitParallel { ... } sum sumParallel // foldParallel + GPars - 23Transitive means result is automatically transparent; Lazy means fails fast FormoredetailsseeReGinAortheGParsdocumentation
  • 24. GPars: Map-Reduce GPars - 24 ©ASERT2006-2013 import static groovyx.gpars.GParsPool.withPool withPool { def oneStarters = (1..30).parallel .map { it ** 2 } .filter { it ==~ '1.*' } assert oneStarters.collection == [1, 16, 100, 121, 144, 169, 196] // aggregations/reductions assert oneStarters.max() == 196 assert oneStarters.reduce { a, b -> a + b } == 747 assert oneStarters.sum() == 747 }
  • 25. GPars parallel array methods Method Return Type combine(initValue) { ... } Map filter { ... } Parallel array collection Collection groupBy { ... } Map map { ... } Parallel array max() T max { ... } T min() T min { ... } T reduce { ... } T reduce(seed) { ... } T size() int sort { ... } Parallel array sum() T parallel // on a Collection Parallel array GPars - 25 FormoredetailsseeReGinAortheGParsdocumentation
  • 26. Parallel Collections vs Map-Reduce GPars - 26 Fork Fork JoinJoin Map Map Reduce Map Map Reduce Reduce Map Filter FilterMap
  • 27. Concurrency challenge… • Suppose we have the following calculation involving several functions: • And we want to use our available cores … GPars - 27 ©ASERT2006-2013 // example adapted from Parallel Programming with .Net def (f1, f2, f3, f4) = [{ sleep 1000; it }] * 3 + [{ x, y -> x + y }] def a = 5 def b = f1(a) def c = f2(a) def d = f3(c) def f = f4(b, d) assert f == 10
  • 28. …Concurrency challenge… • We can analyse the example’s task graph: GPars - 28 ©ASERT2006-2013 // example adapted from Parallel Programming with .Net def (f1, f2, f3, f4) = [{ sleep 1000; it }] * 3 + [{ x, y -> x + y }] def a = 5 def b = f1(a) def c = f2(a) def d = f3(c) def f = f4(b, d) assert f == 10 f2 f3 f1 f4 aa b c d f
  • 29. …Concurrency challenge… • Manually using asynchronous functions: GPars - 29 ©ASERT2006-2013 // example adapted from Parallel Programming with .Net def (f1, f2, f3, f4) = [{ sleep 1000; it }] * 3 + [{ x, y -> x + y }] import static groovyx.gpars.GParsPool.withPool withPool(2) { def a = 5 def futureB = f1.callAsync(a) def c = f2(a) def d = f3(c) def f = f4(futureB.get(), d) assert f == 10 } f2 f3 f1 f4 aa b c d f
  • 30. …Concurrency challenge • And with GPars Dataflows: GPars - 30 ©ASERT2006-2013 def (f1, f2, f3, f4) = [{ sleep 1000; it }] * 3 + [{ x, y -> x + y }] import groovyx.gpars.dataflow.Dataflows import static groovyx.gpars.dataflow.Dataflow.task new Dataflows().with { task { a = 5 } task { b = f1(a) } task { c = f2(a) } task { d = f3(c) } task { f = f4(b, d) } assert f == 10 } f2 f3 f1 f4 aa b c d f
  • 31. …Concurrency challenge • And with GPars Dataflows: GPars - 31 ©ASERT2006-2013 def (f1, f2, f3, f4) = [{ sleep 1000; it }] * 3 + [{ x, y -> x + y }] import groovyx.gpars.dataflow.Dataflows import static groovyx.gpars.dataflow.Dataflow.task new Dataflows().with { task { f = f4(b, d) } task { d = f3(c) } task { c = f2(a) } task { b = f1(a) } task { a = 5 } assert f == 10 } f2 f3 f1 f4 aa b c d f
  • 32. GPars: Dataflows... GPars - 32 ©ASERT2006-2013 import groovyx.gpars.dataflow.DataFlows import static groovyx.gpars.dataflow.DataFlow.task final flow = new DataFlows() task { flow.result = flow.x + flow.y } task { flow.x = 10 } task { flow.y = 5 } assert 15 == flow.result new DataFlows().with { task { result = x * y } task { x = 10 } task { y = 5 } assert 50 == result } 510 yx *
  • 33. ...GPars: Dataflows... • Evaluating: GPars - 33 ©ASERT2006-2013 import groovyx.gpars.dataflow.DataFlows import static groovyx.gpars.dataflow.DataFlow.task final flow = new DataFlows() task { flow.a = 10 } task { flow.b = 5 } task { flow.x = flow.a - flow.b } task { flow.y = flow.a + flow.b } task { flow.result = flow.x * flow.y } assert flow.result == 75 b 10 5 a +- * result = (a – b) * (a + b) x y Question: what happens if I change the order of the task statements here?
  • 34. ...GPars: Dataflows... • Naive attempt for loops GPars - 34 ©ASERT2006-2013 import groovyx.gpars.dataflow.Dataflows import static groovyx.gpars.dataflow.Dataflow.task final flow = new Dataflows() [10, 20].each { thisA -> [4, 5].each { thisB -> task { flow.a = thisA } task { flow.b = thisB } task { flow.x = flow.a - flow.b } task { flow.y = flow.a + flow.b } task { flow.result = flow.x * flow.y } println flow.result } } // => java.lang.IllegalStateException: A DataflowVariable can only be assigned once. ... task { flow.a = 10 } ... task { flow.a = 20 } Don’t do this! X
  • 35. ...GPars: Dataflows... GPars - 35 ©ASERT2006-2013 import groovyx.gpars.dataflow.DataflowStream import static groovyx.gpars.dataflow.Dataflow.* final streamA = new DataflowStream() final streamB = new DataflowStream() final streamX = new DataflowStream() final streamY = new DataflowStream() final results = new DataflowStream() operator(inputs: [streamA, streamB], outputs: [streamX, streamY]) { a, b -> streamX << a - b; streamY << a + b } operator(inputs: [streamX, streamY], outputs: [results]) { x, y -> results << x * y } [[10, 20], [4, 5]].combinations().each{ thisA, thisB -> task { streamA << thisA } task { streamB << thisB } } 4.times { println results.val } b 10 10 20 20 4 5 4 5 a +- * 84 75 384 375
  • 36. ...GPars: Dataflows • Suitable when: – Your algorithms can be expressed as mutually- independent logical tasks • Properties: – Inherently safe and robust (no race conditions or livelocks) – Amenable to static analysis – Deadlocks “typically” become repeatable – “Beautiful” (declarative) code GPars - 36 ©ASERT2006-2013 import groovyx.gpars.dataflow.Dataflows import static groovyx.gpars.dataflow.Dataflow.task final flow = new Dataflows() task { flow.x = flow.y } task { flow.y = flow.x }
  • 37. GPars: Dataflow Sieve GPars - 37 ©ASERT2006-2013 final int requestedPrimeNumberCount = 1000 final DataflowStream initialChannel = new DataflowStream() task { (2..10000).each { initialChannel << it } } def filter(inChannel, int prime) { def outChannel = new DataflowStream() operator([inputs: [inChannel], outputs: [outChannel]]) { if (it % prime != 0) { bindOutput it } } return outChannel } def currentOutput = initialChannel requestedPrimeNumberCount.times { int prime = currentOutput.val println "Found: $prime" currentOutput = filter(currentOutput, prime) } Source: http://groovyconsole.appspot.com/script/235002
  • 38. GPars: Actors... • Actors provide explicit coordination: they don’t share state, instead coordinating via asynchronous messages – Contrasting with predefined coordination for fork/join & map/filter/reduce & implicit coordination for dataflow – Messages are processed one at a time normally in the order they were sent (which is non-deterministic due to asynchronous nature) – Some actor systems allowing message delivery to be prioritised; others allow for sharing some (readonly) state; some allow remote actors for load balancing/robustness • Not new in concept – But has received recent publicity due to special support in Erlang, Scala and other languages GPars - 38 ©ASERT2006-2013
  • 39. …GPars: Actors... • Class with the following lifecycle & methods – But also DSL sugar & enhancements GPars - 39 ©ASERT2006-2013 start() stop() act() send(msg) sendAndWait(msg) loop { } react { msg -> } msg.reply(replyMsg) receive() join()
  • 40. …GPars: Actors... GPars - 40 ©ASERT2006-2013 import groovyx.gpars.actor.DynamicDispatchActor class VotingActor extends DynamicDispatchActor { void onMessage(String language) { processVote(language) } void onMessage(List languages) { languages.each{ processVote it } } private processVote(language) { if (language.startsWith('G')) println "You voted for $language" else println 'Sorry, please try again' } } final votes = new VotingActor().start() votes << 'Groovy' votes << 'C++' votes << ['Groovy', 'Go', 'Dart'] votes.stop() votes.join() You voted for Groovy Sorry, please try again You voted for Groovy You voted for Go Sorry, please try again
  • 41. …GPars: Actors... GPars - 41 ©ASERT2006-2013 import static groovyx.gpars.actor.Actors.* def votes = reactor { it.endsWith('y') ? "You voted for $it" : "Sorry, please try again" } println votes.sendAndWait('Groovy') println votes.sendAndWait('JRuby') println votes.sendAndWait('Go') def languages = ['Groovy', 'Dart', 'C++'] def booth = actor { languages.each{ votes << it } loop { languages.size().times { react { println it } } stop() } } booth.join(); votes.stop(); votes.join() You voted for Groovy You voted for JRuby Sorry, please try again You voted for Groovy Sorry, please try again Sorry, please try again
  • 42. …GPars: Actors GPars - 42 ©ASERT2006-2013 import groovyx.gpars.activeobject.* @ActiveObject class VotingActiveObject { @ActiveMethod vote(String language) { processVote(language) } @ActiveMethod vote(List<String> languages) { languages.collect{ processVote it } } private processVote(language) { if (language.size() == 6) "You voted for $language" else 'Sorry, please try again' } } def voter = new VotingActiveObject() def result1 = voter.vote('Scala') def result2 = voter.vote('Groovy') def result3 = voter.vote(['Pascal', 'Clojure', 'Groovy']) [result1.get(), result2.get(), *result3.get()].each{ println it } Sorry, please try again You voted for Groovy You voted for Pascal Sorry, please try again You voted for Groovy
  • 43. Agents... • Agents safeguard non-thread safe objects • Only the agent can update the underlying object • “Code” to update the protected object is sent to the agent • Can be used with other approaches GPars - 43 ©ASERT2006-2013
  • 44. …Agents… GPars - 44 ©ASERT2006-2013 def random = new Random() def randomDelay = { sleep random.nextInt(10) } String result = '' ('a'..'z').each { letter -> Thread.start{ randomDelay() result += letter } } sleep 100 // poor man's join println result println result.size() Unsafe!
  • 45. …Agents… GPars - 45 ©ASERT2006-2013 import groovyx.gpars.agent.Agent def random = new Random() def randomDelay = { sleep random.nextInt(10) } def agent = new Agent<String>('') ('a'..'z').each { letter -> Thread.start{ randomDelay() agent.send{ updateValue it + letter } } } sleep 100 // poor man's join String result = agent.val println result println result.size()
  • 46. …Agents GPars - 46 ©ASERT2006-2013 import groovyx.gpars.agent.Agent def random = new Random() def randomDelay = { sleep random.nextInt(10) } def agent = new Agent<String>('') def threads = ('a'..'z').collect { letter -> Thread.start { randomDelay() agent.send{ updateValue it << letter } } } threads*.join() String result = agent.val println result println result.size()
  • 47. Software Transactional Memory… GPars - 47 ©ASERT2006-2013 @Grab('org.multiverse:multiverse-beta:0.7-RC-1') import org.multiverse.api.references.LongRef import static groovyx.gpars.stm.GParsStm.atomic import static org.multiverse.api.StmUtils.newLongRef class Account { private final LongRef balance Account(long initial) { balance = newLongRef(initial) } void setBalance(long newBalance) { if (newBalance < 0) throw new RuntimeException("not enough money") balance.set newBalance } long getBalance() { balance.get() } } // ...
  • 48. …Software Transactional Memory GPars - 48 ©ASERT2006-2013 // ... def from = new Account(20) def to = new Account(20) def amount = 10 def watcher = Thread.start { 15.times { atomic { println "from: ${from.balance}, to: ${to.balance}" } sleep 100 } } sleep 150 try { atomic { from.balance -= amount to.balance += amount sleep 500 } println 'transfer success' } catch(all) { println all.message } atomic { println "from: $from.balance, to: $to.balance" } watcher.join()
  • 49. Topics • Intro • Useful Groovy features for Concurrency • GPars Case Studies Web Testing Word Split • More Info • Bonus Material GPars - 49 ©ASERT2006-2013
  • 50. GPars for testing GPars - 50 ©ASERT2006-2013 @Grab('net.sourceforge.htmlunit:htmlunit:2.6') import com.gargoylesoftware.htmlunit.WebClient @Grab('org.codehaus.gpars:gpars:0.10') import static groovyx.gpars.GParsPool.* def testCases = [ ['Home', 'Bart', 'Content 1'], ['Work', 'Homer', 'Content 2'], ['Travel', 'Marge', 'Content 3'], ['Food', 'Lisa', 'Content 4'] ] withPool(3) { testCases.eachParallel{ category, author, content -> postAndCheck category, author, content } } private postAndCheck(category, author, content) { ...
  • 51. Topics • Intro • Useful Groovy features for Concurrency • GPars Case Studies Web Testing Word Split • More Info • Bonus Material GPars - 51 ©ASERT2006-2013
  • 52. Word Split with Fortress GPars - 52 ©ASERT2006-2013 Guy Steele’s StrangeLoop keynote (from slide 52 onwards for several slides): http://strangeloop2010.com/talk/presentation_file/14299/GuySteele-parallel.pdf
  • 53. Word Split… GPars - 53 ©ASERT2006-2013 def swords = { s -> def result = [] def word = '' s.each{ ch -> if (ch == ' ') { if (word) result += word word = '' } else word += ch } if (word) result += word result } assert swords("This is a sample") == ['This', 'is', 'a', 'sample'] assert swords("Here is a sesquipedalian string of words") == ['Here', 'is', 'a', 'sesquipedalian', 'string', 'of', 'words']
  • 54. Word Split… GPars - 54 ©ASERT2006-2013 def swords = { s -> def result = [] def word = '' s.each{ ch -> if (ch == ' ') { if (word) result += word word = '' } else word += ch } if (word) result += word result }
  • 55. Word Split… GPars - 55 ©ASERT2006-2013 def swords = { s -> def result = [] def word = '' s.each{ ch -> if (ch == ' ') { if (word) result += word word = '' } else word += ch } if (word) result += word result }
  • 56. …Word Split… GPars - 56 ©ASERT2006-2013
  • 57. …Word Split… GPars - 57 ©ASERT2006-2013
  • 58. Segment(left1, m1, right1) Segment(left2, m2, right2) Segment(left1, m1 + [ ? ] + m2, right2) …Word Split… GPars - 58 ©ASERT2006-2013
  • 59. …Word Split… GPars - 59 ©ASERT2006-2013 class Util { static maybeWord(s) { s ? [s] : [] } } import static Util.* @Immutable class Chunk { String s public static final ZERO = new Chunk('') def plus(Chunk other) { new Chunk(s + other.s) } def plus(Segment other) { new Segment(s + other.l, other.m, other.r) } def flatten() { maybeWord(s) } } @Immutable class Segment { String l; List m; String r public static final ZERO = new Segment('', [], '') def plus(Chunk other) { new Segment(l, m, r + other.s) } def plus(Segment other) { new Segment(l, m + maybeWord(r + other.l) + other.m, other.r) } def flatten() { maybeWord(l) + m + maybeWord(r) } }
  • 60. …Word Split… GPars - 60 ©ASERT2006-2013 def processChar(ch) { ch == ' ' ? new Segment('', [], '') : new Chunk(ch) } def swords(s) { s.inject(Chunk.ZERO) { result, ch -> result + processChar(ch) } } assert swords("Here is a sesquipedalian string of words").flatten() == ['Here', 'is', 'a', 'sesquipedalian', 'string', 'of', 'words']
  • 61. …Word Split… GPars - 61 ©ASERT2006-2013
  • 62. …Word Split… GPars - 62 ©ASERT2006-2013
  • 63. …Word Split… GPars - 63 ©ASERT2006-2013 THREADS = 4 def pwords(s) { int n = (s.size() + THREADS - 1) / THREADS def map = new ConcurrentHashMap() (0..<THREADS).collect { i -> Thread.start { def (min, max) = [ [s.size(), i * n].min(), [s.size(), (i + 1) * n].min() ] map[i] = swords(s[min..<max]) } }*.join() (0..<THREADS).collect { i -> map[i] }.sum().flatten() }
  • 64. …Word Split… GPars - 64 ©ASERT2006-2013 import static groovyx.gpars.GParsPool.withPool THRESHHOLD = 10 def partition(piece) { piece.size() <= THRESHHOLD ? piece : [piece[0..<THRESHHOLD]] + partition(piece.substring(THRESHHOLD)) } def pwords = { input -> withPool(THREADS) { partition(input).parallel.map(swords).reduce{ a, b -> a + b }.flatten() } }
  • 65. …Guy Steele example in Groovy… GPars - 65 ©ASERT2006-2013 def words = { s -> int n = (s.size() + THREADS - 1) / THREADS def min = (0..<THREADS).collectEntries{ [it, [s.size(),it*n].min()] } def max = (0..<THREADS).collectEntries{ [it, [s.size(),(it+1)*n].min()] } def result = new DataFlows().with { task { a = swords(s[min[0]..<max[0]]) } task { b = swords(s[min[1]..<max[1]]) } task { c = swords(s[min[2]..<max[2]]) } task { d = swords(s[min[3]..<max[3]]) } task { sum1 = a + b } task { sum2 = c + d } task { sum = sum1 + sum2 } println 'Tasks ahoy!' sum } switch(result) { case Chunk: return maybeWord(result.s) case Segment: return result.with{ maybeWord(l) + m + maybeWord(r) } } } DataFlow version: partially hard-coded to 4 partitions for easier reading
  • 66. …Guy Steele example in Groovy… GPars - 66 ©ASERT2006-2013 GRANULARITY_THRESHHOLD = 10 THREADS = 4 println GParsPool.withPool(THREADS) { def result = runForkJoin(0, input.size(), input){ first, last, s -> def size = last - first if (size <= GRANULARITY_THRESHHOLD) { swords(s[first..<last]) } else { // divide and conquer def mid = first + ((last - first) >> 1) forkOffChild(first, mid, s) forkOffChild(mid, last, s) childrenResults.sum() } } switch(result) { case Chunk: return maybeWord(result.s) case Segment: return result.with{ maybeWord(l) + m + maybeWord(r) } } } Fork/Join version
  • 67. …Guy Steele example in Groovy GPars - 67 ©ASERT2006-2013 println GParsPool.withPool(THREADS) { def ans = input.collectParallel{ processChar(it) }.sum() switch(ans) { case Chunk: return maybeWord(ans.s) case Segment: return ans.with{ maybeWord(l) + m + maybeWord(r) } } } Just leveraging the algorithm’s parallel nature
  • 68. Topics • Intro • Useful Groovy features for Concurrency • Gpars • Case Studies More Info • Bonus Material GPars - 68 ©ASERT2006-2013
  • 69. More Information about Concurrency • Web sites – http://gpars.codehaus.org/ – http://g.oswego.edu/ Doug Lea's home page – http://gee.cs.oswego.edu/dl/concurrency-interest/ – http://jcip.net/ Companion site for Java Concurrency in Practice – http://www.eecs.usma.edu/webs/people/okasaki/pubs.html#cup98 Purely Functional Data Structures – http://delicious.com/kragen/concurrency Concurrency bookmark list – http://www.gotw.ca/publications/concurrency-ddj.htm The Free Lunch is Over, Herb Sutter – http://manticore.cs.uchicago.edu/papers/damp07.pdf – http://mitpress.mit.edu/catalog/item/default.asp?ttype=2&tid=10142 Concepts, Techniques, and Models of Computer Programming GPars - 69
  • 70. More Information about Groovy • Web sites – http://groovy.codehaus.org – http://grails.codehaus.org – http://pleac.sourceforge.net/pleac_groovy (many examples) – http://www.asert.com.au/training/java/GV110.htm (workshop) • Mailing list for users – user@groovy.codehaus.org • Information portals – http://www.aboutgroovy.org – http://www.groovyblogs.org • Documentation (1000+ pages) – Getting Started Guide, User Guide, Developer Guide, Testing Guide, Cookbook Examples, Advanced Usage Guide • Books – Several to choose from ... GPars - 70
  • 71. More Information: Groovy in Action GPars - 71 Contains a chapter on GPars!
  • 72. Bonus Material • Other concurrency options • Dining Philosopher Case Study GPars - 72 ©ASERT2006-2013
  • 73. Bonus Material • Other concurrency options – Jetlang – JPPF – Multiverse – Gruple – Cascading – GridGain – ConTest GPars - 73 ©ASERT2006-2013
  • 74. Lightweight threads: Jetlang • Jetlang – A high performance threading library – http://code.google.com/p/jetlang/ GPars - 74 import org.jetlang.fibers.ThreadFiber import org.jetlang.channels.MemoryRequestChannel import org.jetlang.channels.AsyncRequest def req = new ThreadFiber() // or pool def reply = new ThreadFiber() def channel = new MemoryRequestChannel() req.start() reply.start() channel.subscribe(reply) { it.reply(it.request.sum()) } AsyncRequest.withOneReply(req, channel, [3, 4, 5]) { println it } sleep 1000 req.dispose() reply.dispose() 12
  • 75. Other High-Level Libraries: JPPF – Open source Grid Computing platform – http://www.jppf.org/ GPars - 75 import org.jppf.client.* import java.util.concurrent.Callable class Task implements Callable, Serializable { private static final long serialVersionUID = 1162L public Object call() { println 'Executing Groovy' "Hello JPPF from Groovy" } } def client = new JPPFClient() def job = new JPPFJob() def task = new Task() job.addTask task def results = client.submit(job) for (t in results) { if (t.exception) throw t.exception println "Result: " + t.result }
  • 76. Other High-Level Libraries: Gruple... – http://code.google.com/p/gruple – Simple abstraction to coordinate and synchronize threads with ease – based on Tuplespaces • Tuplespaces provide the illusion of a shared memory on top of a message passing system, along with a small set of operations to greatly simplify parallel programming – Example Tuple: [fname:"Vanessa", lname:"Williams", project:"Gruple"] – Basic operations within a Tuplespace are: • put - insert a tuple into the space • get - read a tuple from the space (non-destructively) • take - take a tuple from the space (a destructive read) – Further reading: Eric Freeman, Susanne Hupfer, and Ken Arnold. JavaSpaces Principles, Patterns, and Practice, Addison Wesley, 1999 GPars - 76
  • 77. …Other High-Level Libraries: Gruple... GPars - 77 import org.gruple.SpaceService def defaultSpace = SpaceService.getSpace() defaultSpace << [fname:"Vanessa", lname:"Williams", project:"Gruple"] println defaultSpace.get(fname:"Vanessa", lname:"Williams", project:"Gruple") [project:Gruple, lname:Williams, fname:Vanessa]
  • 78. Other High-Level Libraries: ...Gruple... – Mandelbrot example (included in Gruple download) GPars - 78 ... Space space = SpaceService.getSpace("mandelbrot") Map template = createTaskTemplate() Map task String threadName = Thread.currentThread().name while(true) { ArrayList points task = space.take(template) println "Worker $threadName got task ${task['start']} for job ${task['jobId'] points = calculateMandelbrot(task) Map result = createResult(task['jobId'], task['start'], points) println "Worker $threadName writing result for task ${result['start']} for jo space.put(result) } ...
  • 79. Other High-Level Libraries: ...Gruple GPars - 79
  • 80. Other High-Level Libraries: Cascading.groovy – API/DSL for executing tasks on a Hadoop cluster – http://www.cascading.org/ GPars - 80 def assembly = builder.assembly(name: "wordcount") { eachTuple(args: ["line"], results: ["word"]) { regexSplitGenerator(declared: ["word"], pattern: /[.,]*s+/) } group(["word"]) everyGroup(args: ["word"], results: ["word", "count"]) { count() } group(["count"], reverse: true) } def map = builder.map() { source(name: "wordcount") { hfs(input) { text(["line"]) } } sink(name: "wordcount") { hfs(output) { text() } } } def flow = builder.flow(name: "wordcount", map: map, assembly: assembly)
  • 81. Other High-Level Libraries: GridGain… – Simple & productive to use grid computing platform – http://www.gridgain.com/ GPars - 81 class GridHelloWorldGroovyTask extends GridTaskSplitAdapter<String, Integer> { Collection split(int gridSize, Object phrase) throws GridException { // ... } Object reduce(List results) throws GridException { // ... } } import static GridFactory.* start() def grid = getGrid() def future = grid.execute(GridHelloWorldGroovyTask, "Hello World") def phraseLen = future.get() stop(true)
  • 82. …Other High-Level Libraries: GridGain • http://gridgain.blogspot.com/2010/10/worlds-shortest-mapreduce- app.html GPars - 82 words = "Counting Letters In This Phrase".split(' ') map = new C1() { def apply(word) { word.size() } } reduce = sumIntReducer() println grid.forkjoin(SPREAD, yield(words, map), reduce) // => 27 grid.forkjoin(SPREAD,yield("Counting Letters In This Phrase".split(' '), new C1(){def apply(w){w.size()}}),sumReducer())
  • 83. Testing multi-threaded applications: ConTest... • Advanced Testing for Multi-Threaded Applications – Tool for testing, debugging, and coverage-measuring of concurrent programs (collects runtime statistics) – Systematically and transparently (using a java agent) schedules the execution of program threads in ways likely to reveal race conditions, deadlocks, and other intermittent bugs (collectively called synchronization problems) with higher than normal frequency – The ConTest run-time engine adds heuristically controlled conditional instructions (adjustable by a preferences file) that force thread switches, thus helping to reveal concurrent bugs. You can use existing tests and run ConTest multiple times – by default different heuristics used each time it is run • http://www.alphaworks.ibm.com/tech/contest GPars - 83
  • 84. ...Testing multi-threaded applications: ConTest GPars - 84 NUM = 5 count = 0 def incThread = { n -> Thread.start{ sleep n*10 //synchronized(ParalInc) { count++ //} } } def threads = (1..NUM).collect(incThread) threads.each{ it.join() } assert count == NUM targetClasses = ParalInc timeoutTampering = true noiseFrequency = 500 strength = 10000 Exception in thread "main" Assertion failed: assert count == NUM | | | 4 | 5 false > groovyc ParalInc.groovy > java -javaagent:../../Lib/ConTest.jar -cp %GROOVY_JAR%;. ParalInc ParalInc.groovy
  • 85. Bonus Material • Dining Philosopher’s Case Study – GPars Actors – GPars CSP – Multiverse – Jetlang – Gruple GPars - 85 ©ASERT2006-2013
  • 86. Dining Philosophers… GPars - 86 Philosopher Thinking | Eating Philosopher Thinking | Eating Philosopher Thinking | Eating Philosopher Thinking | Eating Philosopher Thinking | Eating Fork Available | InUse Fork Available | InUse Fork Available | InUse Fork Available | InUse Fork Available | InUse
  • 87. …Dining Philosophers GPars - 87 Philosopher Thinking | Eating Philosopher Thinking | Eating Philosopher Thinking | Eating Philosopher Thinking | Eating Philosopher Thinking | Eating Fork Available | InUse Fork Available | InUse Fork Available | InUse Fork Available | InUse Fork Available | InUse Accepted | Rejected Take | Release Take | Release
  • 88. Dining Philosophers: Actors... GPars - 88 ©ASERT2006-2013 // adapted from GPars example, repo: http://git.codehaus.org/gitweb.cgi?p=gpars.git // file: src/test/groovy/groovyx/gpars/samples/actors/DemoDiningPhilosophers.groovy @Grab('org.codehaus.gpars:gpars:0.10') import groovyx.gpars.actor.* import groovy.beans.Bindable def names = ['socrates', 'plato', 'aristotle', 'descartes', 'nietzsche'] Actors.defaultActorPGroup.resize names.size() class Philosopher extends AbstractPooledActor { private random = new Random() String name int timesEaten = 0 def forks @Bindable String status void act() { assert 2 == forks.size() loop { think() forks*.send new Take() react {a -> react {b -> if ([a, b].any {Rejected.isCase it}) { [a, b].find {Accepted.isCase it}?.reply new Release() } else { eat() [a, b]*.reply new Release() } } } } }
  • 89. …Dining Philosophers: Actors... GPars - 89 ©ASERT2006-2013 … void think() { setStatus('thinking') sleep random.nextInt(5000) setStatus('') } void eat() { setStatus("eating ${++timesEaten}") sleep random.nextInt(3000) setStatus('') } String toString() { switch (timesEaten) { case 0: return "$name has starved" case 1: return "$name has eaten once" default: return "$name has eaten $timesEaten times" } } } final class Take {} final class Accepted {} final class Rejected {} final class Release {}
  • 90. …Dining Philosophers: Actors... GPars - 90 ©ASERT2006-2013 … class Fork extends AbstractPooledActor { String name boolean available = true void act() { loop { react {message -> switch (message) { case Take: if (available) { available = false reply new Accepted() } else reply new Rejected() break case Release: assert !available available = true break default: throw new IllegalStateException("Cannot process the message: $message") } } } } } def forks = (1..names.size()).collect { new Fork(name: "Fork $it") } def philosophers = (1..names.size()).collect { new Philosopher(name: names[it - 1], forks: [forks[it - 1], forks[it % names.size()]]) }
  • 91. …Dining Philosophers: Actors GPars - 91 ©ASERT2006-2013 … import groovy.swing.* import java.awt.Font import static javax.swing.JFrame.* def frame = new SwingBuilder().frame(title: 'Philosophers', defaultCloseOperation: EXIT_ON_CLOSE) { vbox { hbox { (0..<names.size()).each { i -> def widget = textField(id: names[i], text: names[i].center(14)) widget.font = new Font(widget.font.name, widget.font.style, 36) philosophers[i].propertyChange = { widget.text = philosophers[i].status.center(14) } } } } } frame.pack() frame.visible = true forks*.start() sleep 1000 philosophers*.start() sleep 10000 forks*.stop() forks*.join() philosophers*.stop() philosophers*.join() frame.dispose() philosophers.each { println it } socrates has eaten 3 times plato has eaten 3 times aristotle has eaten 6 times descartes has eaten 2 times nietzsche has eaten 5 times
  • 92. Dining Philosophers: CSP... GPars - 92 ©ASERT2006-2013 // inspired by similar examples at the web sites below: // http://www.cs.kent.ac.uk/projects/ofa/jcsp/ // http://www.soc.napier.ac.uk/~jmk/#_Toc271192596 @Grab('org.codehaus.gpars:gpars:0.10') import org.jcsp.lang.* import groovyx.gpars.csp.PAR import groovyx.gpars.csp.ALT import static java.lang.System.currentTimeMillis def names = ['socrates', 'plato', 'aristotle', 'descartes', 'nietzsche'] enum ForkAction { Take, Release, Stop } import static ForkAction.* class Philosopher implements CSProcess { ChannelOutput leftFork, rightFork String name def forks = [] private random = new Random() private timesEaten = 0 private start = currentTimeMillis() void run() { while (currentTimeMillis() - start < 10000) { think() eat() } [leftFork, rightFork].each { it.write(Stop) } println toString() } …
  • 93. …Dining Philosophers: CSP... GPars - 93 ©ASERT2006-2013 … void think() { println "$name is thinking" sleep random.nextInt(50) } void eat() { [leftFork, rightFork].each { it.write(Take) } println "$name is EATING" timesEaten++ sleep random.nextInt(200) [leftFork, rightFork].each { it.write(Release) } } String toString() { switch (timesEaten) { case 0: return "$name has starved" case 1: return "$name has eaten once" default: return "$name has eaten $timesEaten times" } } }
  • 94. …Dining Philosophers: CSP... GPars - 94 ©ASERT2006-2013 … class Fork implements CSProcess { ChannelInput left, right private active = [0, 1] as Set void run() { def fromPhilosopher = [left, right] def forkAlt = new ALT(fromPhilosopher) while (active) { def i = forkAlt.select() read fromPhilosopher, i, Take read fromPhilosopher, i, Release } } void read(phil, index, expected) { if (!active.contains(index)) return def m = phil[index].read() if (m == Stop) active -= index else assert m == expected } } …
  • 95. …Dining Philosophers: CSP GPars - 95 ©ASERT2006-2013 … def lefts = Channel.createOne2One(names.size()) def rights = Channel.createOne2One(names.size()) def philosophers = (0..<names.size()).collect { i -> return new Philosopher(leftFork: lefts[i].out(), rightFork: rights[i].out(), name: names[i]) } def forks = (0..<names.size()).collect { i -> return new Fork(left: lefts[i].in(), right: rights[(i + 1) % names.size()].in()) } def processList = philosophers + forks new PAR(processList).run()
  • 96. Why CSP? • Amenable to proof and analysis GPars - 96 Picture source: http://wotug.org/parallel/theory/formal/csp/Deadlock/
  • 97. Multiverse Philosophers… GPars - 97 //@Grab('org.multiverse:multiverse-core:0.7-SNAPSHOT') //@Grab('org.multiverse:multiverse-alpha:0.7-SNAPSHOT') //@Grab('org.multiverse:multiverse-groovy:0.7-SNAPSHOT') //@GrabConfig(systemClassLoader=true, initContextClassLoader = true) // adapted multiverse Groovy example: http://git.codehaus.org/gitweb.cgi?p=multiverse.git // file: multiverse-groovy/src/test/groovy/org/multiverse/integration/ org/multiverse/integration/examples/DiningPhilosphersTest.groovy import org.multiverse.transactional.refs.BooleanRef import org.multiverse.transactional.refs.IntRef import static MultiverseGroovyLibrary.* def food = new IntRef(5) def names = ['socrates', 'plato', 'aristotle', 'descartes', 'nietzsche'] def forks = (1..5).collect { new Fork(id: it, free: new BooleanRef(true)) } def philosophers = (0..4).collect { new Philosopher(name: names[it], food: food, left: forks[(it + 1) % 5], right: forks[it]) } def threads = philosophers.collect { new Thread(it) } threads*.start() threads*.join() philosophers.each { println it } class Fork { int id BooleanRef free void take() { free.set(false) } void release() { free.set(true) } }
  • 98. …Multiverse Philosophers GPars - 98 class Philosopher implements Runnable { String name Fork left, right IntRef timesEaten = new IntRef() IntRef food void eat() { atomic(trackreads: true, explicitRetryAllowed: true) { left.free.await(true) right.free.await(true) if (food.get() > 0) { left.take(); right.take() timesEaten.inc(); sleep 10; food.dec() } } } void think() { atomic(trackreads: true, explicitRetryAllowed: true) { left.release(); right.release() } sleep 10 } void run() { 10.times { eat(); think() } } String toString() { switch (timesEaten) { case 0: return "$name has starved" case 1: return "$name has eaten once" default: return "$name has eaten $timesEaten times" } } }
  • 99. Jetlang Philosophers… GPars - 99 import org.jetlang.core.Callback import org.jetlang.fibers.ThreadFiber import org.jetlang.channels.* def names = ['socrates', 'plato', 'aristotle', 'descartes', 'nietzsche'] class Philosopher implements Callback { private random = new Random() String name int timesEaten = 0 String status def forks private channels = [new MemoryRequestChannel(), new MemoryRequestChannel()] private req = new ThreadFiber() private reply = new ThreadFiber() private responses = [] private gotFork = { it instanceof Accepted } void start() { assert forks.size() == 2 req.start() reply.start() (0..1).each{ channels[it].subscribe(reply, forks[it]) } think() } String toString() { switch (timesEaten) { case 0: return "$name has starved" case 1: return "$name has eaten once" default: return "$name has eaten $timesEaten times" } }
  • 100. …Jetlang Philosophers… GPars - 100 … void think() { println(name + ' is thinking') sleep random.nextInt(3000) (0..1).each{ AsyncRequest.withOneReply(req, channels[it], new Take(it), this); } } void eat() { timesEaten++ println toString() sleep random.nextInt(2000) } void onMessage(Object message) { responses << message if (responses.size() == 2) { if (responses.every(gotFork)) { eat() } responses.findAll(gotFork).each { int index = it.index channels[index].publish(req, new Release(index), forks[index]) } responses = [] think() } } } @Immutable class Take { int index } @Immutable class Accepted { int index } @Immutable class Rejected { int index } @Immutable class Release { int index } …
  • 101. …Jetlang Philosophers GPars - 101 … class Fork implements Callback { String name def holder = [] void onMessage(message) { def msg = message instanceof Request ? message.request : message def index = msg.index switch (msg) { case Take: if (!holder) { holder << index message.reply(new Accepted(index)) } else message.reply(new Rejected(index)) break case Release: assert holder == [index] holder = [] break default: throw new IllegalStateException("Cannot process the message: $message") } } } def forks = (1..names.size()).collect { new Fork(name: "Fork $it") } def philosophers = (1..names.size()).collect { new Philosopher(name: names[it - 1], forks: [forks[it - 1], forks[it % names.size()]]) } philosophers*.start() sleep 10000 philosophers.each { println it }
  • 102. Gruple Philosophers… GPars - 102 import org.gruple.SpaceService import org.gruple.Space class Philosopher { private random = new Random() String name Space space private timesEaten = 0 int id, num boolean done = false void run() { while (true) { think() if (done) return space.take(fork: id) space.take(fork: (id + 1) % num) eat() space.put(fork: id) space.put(fork: (id + 1) % num) } } void think() { println "$name is thinking" sleep random.nextInt(500) } void eat() { println "$name is EATING" timesEaten++ sleep random.nextInt(1000) } … … socrates is thinking nietzsche is thinking descartes is EATING aristotle is EATING descartes is thinking plato is EATING aristotle is thinking socrates is EATING plato is thinking nietzsche is EATING socrates is thinking nietzsche is thinking descartes is EATING descartes is thinking socrates has eaten 5 times plato has eaten 4 times aristotle has eaten 4 times descartes has eaten 4 times nietzsche has eaten 5 times
  • 103. …Gruple Philosophers GPars - 103 … String toString() { switch (timesEaten) { case 0: return "$name has starved" case 1: return "$name has eaten once" default: return "$name has eaten $timesEaten times" } } } def names = ['socrates', 'plato', 'aristotle', 'descartes', 'nietzsche'] def diningSpace = SpaceService.getSpace('Dining') def philosophers = (0..<names.size()).collect{ new Philosopher(name: names[it], id: it, space: diningSpace, num: names.size()) } (0..<names.size()).each{ diningSpace << [fork: it] } sleep 500 def threads = (0..<names.size()).collect{ n -> Thread.start{ philosophers[n].run() } } sleep 10000 philosophers*.done = true sleep 2000 threads.join() println() philosophers.each{ println it }