Programming Languages: Chapter 14: Concurrency and Synchronization



Sequential to concurrent language evolution


Scope for concurrency in presented languages


Coroutines in Lua


Communicating Sequential Processes

    A quick reference guide to the CSP model of concurrency in the Go programming language is available here. synchronous (i.e., unbuffered) versus asynchronous (i.e., buffered, `at any time') communication.


    Essential idioms and actions of goroutines performed on channels


    Communicating sequential processes in Go


    Notable Features of Channels

    1. channels provide a mechanism for inter-thread communication,
    2. channels are typed (i.e., the programmer can declare a channel of ints, strings, or Philosopher* pointers),
    3. channels can be buffered (i.e., asynchronous) or unbuffered (i.e., synchronous),
    4. access to channels by goroutines is atomic (i.e., mutually exclusive); locking/unlocking is unnecessary,
    5. reads and writes to channels automatically block and unblock depending on the context (i.e., mutual exclusivity is built into the channel), and
    6. channels are open for both reading and writing by default, but either end of the channel can be closed for one of those operations (i.e., can be read-only or write-only)


    Modeling Table in Dining Philosophers




Actor Model of Concurrency

    A quick reference sheet for the Elixir programming language is available at https://media.pragprog.com/titles/elixir/ElixirCheat.pdf.


    OOP Model vis-à-vis Actor Model

    Conceptual differences between the object-oriented model of programming (depicted left) and the actor model of concurrent programming (depicted right). Key: ◯ = object (on left) & actor (on right), → = inactive message, and ⇢ = active message or current thread of control.

    Object-oriented Model of Sequential Programming           Actor Model of Concurrent Programming
             


    The three primary actions of an actor


    cond vis-à-vis if .. else

    cond do
       C_1 -> B_1
       true -> B_2
    end
    
    is same as
    if C_1
      B_1
    else
      B_2
    


    More generally: cond vis-à-vis if .. else

    cond do
       C_1 -> B_1
       C_2 -> B_2
       C_3 -> B_3
          ...
       C_n -> B_n
       true -> B_n+1
    end
    
    
    is same as
    if C_1
      B_1
    else if C_2
            B_2
         else if C_3
                 B_3
              else if ...
                      ...
                   else if C_n
                           B_n
                        else
                            B_n+1
    


    Are Actors (Heavyweight) Processes?

    In Erlang, and therefore Elixir, an actor is called a process. In most environments a process is a heavyweight entity that consumes lots of resources and is expensive to create. An Elixir process, by contrast, is very lightweight-lighter weight even than most systems' threads, both in terms of resource consumption and startup cost. Elixir programs typically create thousands of processes without problems and don't normally need to resort to the equivalent of thread pools (ref. [SCMSW] Chapter 5: Actors, p. 116).


    Pattern Matching in Elixir

    defmodule Pattern do
    
       def head([]) do
          IO.puts("error")
       end
       def head([a|b]) do
          a
       end
    
       def third([a,b,c|d]) do
          c
       end
    
       def tail([a|b]) do
          b
       end
       def tail1(l) do
          tl(l)
       end
    
       def listlen([]) do
          0
       end
    
       def listlen([a|b]) do
          1 + listlen(b)
       end
    
       def ourmap([],f) do
         []
       end       
    
       def ourmap([h|t],f) do
           [f.(h)|ourmap(t,f)]
       end       
    end
    


    Mailboxes are Organized in FIFO Order

    import :timer, only: [ sleep: 1 ]
    
    defmodule TestFIFO do
    
      def start_actor() do
         spawn_link(__MODULE__, :actor_loop, [true])
      end
      
      def actor_loop(first) do
        cond do
           first -> IO.puts("Actor just started.")
                    sleep(7000)
                    IO.puts("Actor done sleeping and ready to receive.")
           true -> nil
        end
        receive do
          {:d, 1} -> IO.puts("Received message d 1.")
          {:d, 2} -> IO.puts("Received message d 2.")
          {:c} -> IO.puts("Received message c.")
          {:b} -> IO.puts("Received message b.")
          {:a} -> IO.puts("Received message a.")
        end
        actor_loop(false)
      end
    end
    
    actor = TestFIFO.start_actor()
    send(actor, {:a})
    IO.puts("Main just sent message a.")
    send(actor, {:b})
    IO.puts("Main just sent message b.")
    send(actor, {:c})
    IO.puts("Main just sent message c.")
    send(actor, {:d, 1})
    IO.puts("Main just sent message d 1.")
    send(actor, {:d, 2})
    IO.puts("Main just sent message d 2.")
    sleep(10000)
    exit(:normal)
    
    $ iex TestFIFO.ex 
    Erlang/OTP 20 [erts-9.1] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:10] [hipe] [kernel-poll:false]
    
    Main just sent message a.
    Actor just started.
    Main just sent message b.
    Main just sent message c.
    Main just sent message d 1.
    Main just sent message d 2.
    Actor done sleeping and ready to receive.
    Received message a.
    Received message b.
    Received message c.
    Received message d 1.
    Received message d 2.
    $
    


    Mapping in Parallel

    defmodule Parallel do
    
       def map(collection, fun) do
    
          parent = self()
    
          processes = Enum.map(collection,
                               fn(e) ->
                                  spawn_link(fn() ->
                                               send(parent, {self(), fun.(e)})
                                             end)
                               end)
    
          Enum.map(processes, fn(pid) ->
                                 receive do
                                    {^pid, result} -> result
                                 end
                              end)
       end
    end
    
    #slow_double = fn(x) -> :timer.sleep(1000); x*2 end
    #:timer.tc(fn() -> Enum.map([1,2,3,4], slow_double) end)
    #:timer.tc(fn() -> Parallel.map([1,2,3,4], slow_double) end)
    
    (ref. [SCMSW] Chapter 5: Actors, p. 125)


    Interpreting Graphs of Actors and Messages

    An incoming arrow into an actor represents a message in a receive block. An outgoing arrow from an actor represents a send function, where the receipant of the message in the first argument to send is the target of the arrow in the graph.


    Queue Functions in Elixir

    # q is FIFO queue
    
    q =r:queue.new() # creates a new FIFO queue; returns a queue q
    :queue.is_empty(q) # returns a boolean
    q = :queue.in({a,b}, q) # is a push operation; returns a new queue q
    q = :queue.drop(q) # is a pop operation; returns the tail of the queue q
    {a,b} = :queue.get(q) # is a top operation; returns the head of the queue q
    size = :queue.len(q) # is a size operation; returns the size of the queue q
    
    # IMPORTANT: none of these functions alter/modify their queue argument
    
    #Examples:
    
    # roomqueue is FIFO queue
    
    roomqueue = :queue.new() creates a new queue
    roomqueue = :queue.in({customer, id}, roomqueue)
    {customer, id} = :queue.get(roomqueue)
    roomqueue = :queue.drop(roomqueue)
    size = :queue.len(roomqueue) returns the length of the queue
    


    Model of Actors and Messages in the Sleeping Barber


    A Synchronization Barrier


    Model of Actors and Messages in the Barrier


    Model of Actors and Messages in Parallel Matrix Multiplication


A Comparison of Go and Elixir for concurrent programming.

Concept Go Elixir
concurrent entities anonymous goroutines (lightweight threads) Actors (lightweight threads) with identity
inter-thread communication mechanism channels messages and mailboxes
inter-thread communication style synchronous or asynchronous asynchronous
spawning threads go f(n) spawn_link(__MODULE__, :f, [n])
waiting for child threads use a wait group &sync.WaitGroup{} or
a synchronous channel var done = make(chan int)
use Process.flag(:trap_exit, true)
with a receive do ... end block


Summary of concurrent languages


Comparison of thread creation and communication in C, Java, Go, Elixir, Julia, Lua, and Io.


Detailed comparison of thread creation and communication in Go and Elixir


Comparison of thread creation and communication in C, Java, Go, & Elixir


References

        [SLSW] Seven Languages in Seven Weeks: A Pragmatic Guide to Learning Programming Languages by B.A. Tate. Pragmatic Programmers, Dallas, TX, 2010. ISBN-13:978-1-934356-59-3 (textbook webpage contains links to the source code of all programs in the text).
        [SCMSW] Seven Concurrency Models in Seven Weeks: When Threads Unravel by P. Butcher. Pragmatic Programmers, Dallas, TX, 2014. ISBN-13:978-1-937785-65-9 (textbook webpage contains links to the source code of all programs in the text). An eBook of [SCMSW] is available free to all UD students in the library's eContent collection. To access it conduct a search for the title in the library's catalog at library.udayton.edu.

    Steps to access [SCMSW] off-campus:
    1. Go to https://login.libproxy.udayton.edu/menu.
    2. Login using your standard UD credentials.
    3. Click on the link labeled: EBSCO Ebook Collection (formerly NetLibrary).
    4. This link will will take you to a page were you can perform a search. Search for "Seven concurrency models in seven weeks: When threads unravel" Click on "eBook Full Text".

      Note that there is a limit on how many people can view the book and once someone checks it out, no one else can use it. Also, if you incorrectly break your session (closing the page), the book remains checked out for a period of time and you cannot use the book in until the session ends.
        [SMLSW] Seven More Languages in Seven Weeks: Languages that are Shaping the Future by B.A. Tate, F. Daoud, I. Dees, & J. Moffit. Pragmatic Programmers, Dallas, TX, 2014. ISBN-13:978-1-941222-15-7 (textbook webpage contains links to the source code of all programs in the text). An eBook of [SMLSW] is available free to all UD students in the library's eContent collection. To access it conduct a search for the title in the library's catalog at library.udayton.edu. Steps to access [SMLSW] off-campus: follow same steps above for accessing [SCMSW] (with the title of this book).


Return Home