Skip to content

Instantly share code, notes, and snippets.

@albertnetymk
Last active August 29, 2015 14:23
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save albertnetymk/5d4740ba525dca47a2a2 to your computer and use it in GitHub Desktop.
Save albertnetymk/5d4740ba525dca47a2a2 to your computer and use it in GitHub Desktop.

class: center, middle

Synchronize with Asynchronous Computation

Albert Mingkun Yang


Block or Continuation, that's the question

  • Block: Easy to read, but waste CPU cycles
  • Continuation: Utilize CPU fully, but introduce callback hell
// Pyramid of Doom
(function($) {
  $(function(){
    $("button").click(function(e) {
      $.get("/test.json", function(data, textStatus, jqXHR) {
        $(".list").each(function() {
          $(this).click(function(e) {
            setTimeout(function() {
              alert("Hello World!");
            }, 1000);
          });
        });
      });
    });
  });
})(jQuery);

Client-Server example

Simulating one server (s) interacting with two clients (c1, c2):

  • Starts with single thread, and explore what changes using two threads.
  • c1 calls heavy on s asynchronously, and blocks on the result
  • heavy is long running method without IO.
  • c2 just prints some string to STDOUT

Using Encore actors:

# pseudocode
class Server
  heavy = ->
    long_running()
    print "heavy"

class Client
  block = ->
    get s.heavy()

  nonblock = ->
    print "nonblock"

s = new Server
c1 = new Client
c2 = new Client
c1.block()
c2.nonblock()

Complete code in: bit.ly/sfm15_encore


Using Java ForkJoinPool:

# pseudocode
s = ->
  long_running()
  print "heavy"

c1 = ->
  s.fork
  pool.execute c2
  s.join

c2 = ->
  print "nonblock"

main = ->
    pool.execute c1

Complete code in: bit.ly/sfm15_fork


Step-by-step walk-through

# pseudocode
s = ->
  long_running()
  print "heavy"

c1 = ->
  s.fork
  pool.execute c2
  s.join

c2 = ->
  print "nonblock"

main = ->
*    pool.execute c1

Submit c1 to the pool.


Step-by-step walk-through

# pseudocode
s = ->
  long_running()
  print "heavy"

*c1 = ->
  s.fork
  pool.execute c2
  s.join

c2 = ->
  print "nonblock"

main = ->
    pool.execute c1

c1 is run by the pool.


Step-by-step walk-through

# pseudocode
s = ->
  long_running()
  print "heavy"

c1 = ->
*  s.fork
  pool.execute c2
  s.join

c2 = ->
  print "nonblock"

main = ->
    pool.execute c1

Fork s so that s and c1 could run in parallel potentially.


Step-by-step walk-through

# pseudocode
s = ->
  long_running()
  print "heavy"

c1 = ->
  s.fork
*  pool.execute c2
  s.join

c2 = ->
  print "nonblock"

main = ->
    pool.execute c1

Submit c2 to the pool.


Step-by-step walk-through

# pseudocode
s = ->
  long_running()
  print "heavy"

c1 = ->
  s.fork
  pool.execute c2
*  s.join

c2 = ->
  print "nonblock"

main = ->
    pool.execute c1

c1 waits until s is finished.


Result

                | encore             | java
--------------- | ------------------ | ------------------
single thread   | heavy; nonblock    | heavy; nonblock
two threads     | nonblock; heavy    | heavy; nonblock

Summary

  • blocking on futures doesn't block the underlying thread, like green thread
  • not optimal if the long running async computation is not long enough
  • auto-switching makes more sense for actors, for actors are concurrent
  • possibly optimization: busy waiting first, then switching if still not done
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment