Skip to content

Instantly share code, notes, and snippets.

@akidee
Created November 11, 2011 21:15
Show Gist options
  • Save akidee/1359300 to your computer and use it in GitHub Desktop.
Save akidee/1359300 to your computer and use it in GitHub Desktop.
Concurrent ressource access
###
Ein Beispiel, und es funktioniert:
###
# global object collection, by ID
objById = {
id1: {}
id2: {}
id3: {}
}
# job that changes 1 object and optionally returns a result (async)
# Result must be that prop includes a list in which after every 1, there must be a 2, and after every 2, there must be a 1.
# Wrong parallel calling can result to several 1s or 2s after another
changeObj = (obj, __) ->
if !obj.prop
obj.prop = []
obj.prop.push(1)
setTimeout(->
obj.prop.push(2)
__(null, obj.prop)
, 100)
queues = ((queues = {}) ->
# global job list
jobsById = {
}
# push a job for an ID
push = queues.push = (id, job, args...) ->
jobs = jobsById[id]
exist = jobs && jobs.length
if !exist
jobs = jobsById[id] = []
# wrap callback, end of job triggers next in queue
# The last arg is always the callback
callback = args.pop()
args.push(
->
callback.apply(null, arguments)
jobs.shift()
shift(id)
)
jobs.push([ job, args ])
# Queue execution is started because queue was clear
if !exist
shift(id)
# Exec the next job for a key
shift = queues.shift = (id) ->
jobs = jobsById[id]
if !jobs || !jobs.length
return
job = jobs[jobs.length - 1]
job[0].apply(null, job[1])
queues
)()
###
# How not to do it
changeObj(objById.id1, (e, r) ->
console.log '1. call: ', r
)
changeObj(objById.id1, (e, r) ->
console.log '2. call: ', r
)
###
# How to do it
queues.push('id1', changeObj, objById.id1, (e, r) ->
console.log '1. call: ', r
)
queues.push('id1', changeObj, objById.id1, (e, r) ->
console.log '2. call: ', r
)
###
Hier sind nun alle Objekte innerhalb eines Prozesses global. Für shared memory zwischen mehreren isolierten prozessen ist da natürlich eine Datenbank und ein globaler Lock (zB Redis mit blockenden Listen) nötig.
###

Problem: eine Ressource, die von mehreren Änderungen parallel betroffen ist. (Mit den Fachbegriffen von shared memory und concurrent ressource access bin ich nicht wirklich vertraut, aber das Problem ist klar.) Beispiel sei ein lokaler Cache von Objekten (die eindeutig identifiziert sind), die anhand eben dieser ID geholt werden, geändert werden, da ja in JS objekte immer per Referenz gegeben sind, also automatisch auch im Cache geändert werden. Hier sollte natürlich dasselbe Objekt zu gg. zeit immer nur von genau einem Job geändert werden, was, falls das in einem nicht-parallelen worker script passiert, sicher auch kein problem darstellt. Sobald dieser Zugriff aber von zeitlich unbestimmten Events ausgelöst wird, gibt es das problem des konkurrenten Zugriffs. Soweit ich weiß, ist das bei synchronen Funktionen (also wenn über den gesamten ändernden Zugriff hinweg keine asynchronen callbacks stattfinden) kein problem, da die interne event queue die Events konsequent nacheinander abarbeitet, aber sobald der Zugriff mit vielen asynchronen Operationen geschieht, ist es doch so, dass Callbacks auch auf tieferer Ebene in die event queue gelangen, und so eben mehrere parallele Ops mit Kontextwechsel quasi-parallel ausgeführt werden und so natürlich das geteilte Objekt in falsche Ausgangszustände gerät. Hier muss explizit das Objekt gelockt werden, indem alle Jobs für eine Dateneinheit in eine queue eingereiht werden (die natürlich genauso global wie die Daten sein muss).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment