[erlang-questions] Question about message passing paradigm
Tim Watson
watson.timothy@REDACTED
Mon Jun 30 15:14:23 CEST 2008
Hi Mike,
some of the more experienced erlangers probably have specific
techniques to help with this, but my general take on it (coming from a
mixed background that includes java/c++ as well as erlang) is to
reconsider this design. I'd move away from the pull style you're using
here, which requires all this synchronization code (although it's much
easier to understand than comparable thread safe java code), and go
for a push style that (a) doesn't involve all this mutable state and
(b) puts the sync code all in one place.
> After being bitten by the pitfalls of lock-oriented multi threading I
> am interested in switching to message passing oriented concurrency.
Indeed, but Erlang's message passing style isn't the only thing about
it that makes concurrent programming easier! The fact that Erlang
follows the functional programming paradigm is also a major
improvement over imperative languages like Java. The key thing here is
that you don't have mutable state to worry about. I'd question why you
need to implement these state containers in the first place - perhaps
there's a more purely functional way of looking at the problem at hand
and a finer abstraction to help deal with it!? I'm sure that posting
some more details might entice people to have a more concrete and
interesting discussion!
Now I'll concede that some Erlang process do encapsulate state that
changes over time (in response to incoming messages) and using your
example of pessimistic concurrency, it's true that in order to
maintain integrity across the 3 "container processes" you'd need some
kind of locking protocol. This can be established very easily by using
a selective receive on B, C and D, to ensure that a request for a
resource with id=X within the process, is locked until the requesting
process (A) releases it. You might, for example, set up a lock list in
your loop, for example:
loop(State, LockList) ->
%% ... some interesting code...
receive
{ SenderPid, acquire, ItemId } ->
{ acquired, Response } = get_item(ItemId, LockList),
SenderPid ! { { acquired, ok }, Response },
loop(State, [ ItemId | LockList ])
; { SenderPid, read, ItemId } ->
SenderPid ! { { read, ok }, get_item_readonly(ItemId) },
loop(State, LockList)
; { SenderPid, write, { ItemId, Value } } ->
{ acquired, Response } = get_item(ItemId, LockList),
Write = write_item(ItemId, Value),
SenderPid ! { { write, ok }, Write }
end.
Ok so it's a very quick and contrived example, but you get the point.
The read should have better concurrent behavior as we only need to
check locks for writes or write locks (acquire) and in this simple
example, I used pattern matches to make sure that failed acquire
and/or write message will cause the process to die. I'm not sure
that's how I'd like to do it in a production system, but it serves as
a very simple example. It might be that you're better off with proper
transaction semantics (such as 2 phase commit), which are quite easy
to write in the message passing style using patterns such as
leader-follower to deal with leader election and transaction voting,
etc.There's lots of examples of this on the web.
Cheers,
Tim Watson
More information about the erlang-questions
mailing list