Multithreaded Programming

Introduction


Multithreaded applications can be built on neko. This tutorial contains three examples of how data can be shared between threads. In each example the main thread will start two other threads. One will send some data to the other, then they'll exit.

Creating and Destroying Threads


Threads are created using neko.vm.Thread.create(doSomething) where doSomething is the function that the new thread will start running. The new thread will start immediately. When the thread exits doSomething it will be destroyed.

Message Passing


In this example, the threads use the neko.vm.Thread.sendMessage() and neko.vm.Thread.readMessage() functions. Note that these functions pass copies of simple types and pass objects by reference.

import neko.vm.Thread;
import neko.Sys;
import neko.Lib;

class MessagePassing
{
  static function main()
  {
    // create two thread, keep references
    Lib.println("Message Passing Example");
    var t1 = Thread.create(sendMsgs);
    var t2 = Thread.create(getMsgs);

    // give thread1 ref to main thread and thread2
    t1.sendMessage(Thread.current());
    t1.sendMessage(t2);

    // give thread2 a ref to main thread
    t2.sendMessage(Thread.current());

    // wait for them to finish
    Thread.readMessage(true);
    Thread.readMessage(true);
    Lib.println("done\n");
  }

  // thread1 sends messages to thread2
  static function sendMsgs()
  {
    // get ref to main and thread2
    var main:Thread = Thread.readMessage(true);
    var t2:Thread   = Thread.readMessage(true);

    for (ii in 0...5)
    {
      Lib.println("t1 sending: " + ii);
      t2.sendMessage(ii);
      Sys.sleep(.5);
    }
    main.sendMessage("thread1 done");
  }

  // thread2 gets messages sent by thread1
  static function getMsgs()
  {
    // get ref to main
    var main:Thread = Thread.readMessage(true);

    for (ii in 0...5)
    {
      Lib.println("t2 waiting for msg");
      Lib.println("t2 got: " + Thread.readMessage(true));
    }
    main.sendMessage("thread2 done");
  }
}

Shared Deque


In this example we will load data into a Deque in one thread and remove it in the other. The Deque handles thread synchronization internally. Sharing data in this way with a List or Array is not recommended.
import neko.vm.Thread;
import neko.vm.Deque;
import neko.Lib;
import neko.Sys;

class SharedDeque
{
  static function main()
  {
    // create deque for passing data
    var obj = new Deque<Int>();

    // create two thread, keep references
    Lib.println("Shared Deque Example");
    var t1 = Thread.create(sendMsgs);
    var t2 = Thread.create(getMsgs);

    // give each thread ref to main thread and deque
    t1.sendMessage(Thread.current());
    t1.sendMessage(obj);
    t2.sendMessage(Thread.current());
    t2.sendMessage(obj);

    // wait for them to finish
    Thread.readMessage(true);
    Thread.readMessage(true);
    Lib.println("done\n");
  }

  // thread1 sends messages to thread2
  static function sendMsgs()
  {
    // get ref to main and deque
    var main:Thread    = Thread.readMessage(true);
    var obj:Deque<Int> = Thread.readMessage(true);

    for (ii in 0...5)
    {
      Lib.println("t1 pushing " + ii);
      obj.push(ii);
      Sys.sleep(.5);
    }
    main.sendMessage("thread1 done");
  }

  // thread2 gets messages sent by thread1
  static function getMsgs()
  {
    // get ref to main thread and deque
    var main:Thread    = Thread.readMessage(true);
    var obj:Deque<Int> = Thread.readMessage(true);

    for (ii in 0...5)
    {
      Lib.println("t2 waiting for msg");
      Lib.println("t2 got: " + obj.pop(true));
    }
    main.sendMessage("thread2 done");
  }
}

Shared Object


In this example both threads share an object reference. The object synchronizes access to its data using a neko.vm.Mutex. This example is a bit different than the others in that the receiving thread doesn't remove the value and wait for the next one. Instead it just reads what's there.
import neko.vm.Thread;
import neko.vm.Mutex;
import neko.Lib;
import neko.Sys;

// an object to share between threads
class Obj
{
  public var m:Mutex;
  public var val(getVal,setVal):Int;
  public function new()
  {
    m = new Mutex();
    val = 0; 
  }

  private function getVal()
  {
    m.acquire();
    var ret = val;
    m.release();
    return ret;
  }

  private function setVal(v:Int)
  {
    m.acquire();
    val = v;
    m.release();
    return v;
  }
}

class UseMutex
{
  static function main()
  {
    // create object to be shared
    var obj:Obj = new Obj();

    // create two thread, keep references
    Lib.println("Shared Object Example");
    var t1 = Thread.create(sendMsgs);
    var t2 = Thread.create(getMsgs);

    // give each thread ref to main thread and shared object
    t1.sendMessage(Thread.current());
    t1.sendMessage(obj);
    t2.sendMessage(Thread.current());
    t2.sendMessage(obj);

    // wait for them to finish
    Thread.readMessage(true);
    Thread.readMessage(true);
    Lib.println("done\n");
  }

  // thread1 sends messages to thread2
  static function sendMsgs()
  {
    // get ref to main and thread2
    var main:Thread = Thread.readMessage(true);
    var obj:Obj     = Thread.readMessage(true);

    for (ii in 0...5)
    {
      Lib.println("t1 setting" + ii);
      obj.val = ii;
      Sys.sleep(.5);
    }
    main.sendMessage("thread1 done");
  }

  // thread2 gets messages sent by thread1
  static function getMsgs()
  {
    var main:Thread = Thread.readMessage(true);
    var obj:Obj     = Thread.readMessage(true);
    var ii = 0;

    while (ii != 4)
    {
      ii = obj.val;
      Lib.println("t2 got: " + ii);
      Sys.sleep(.3);
    }
    main.sendMessage("thread2 done");
  }
}

Results

Compile and run the examples with:

-neko messagepassing.n
-main MessagePassing
-cmd neko messagepassing.n

--next

-neko shareddeque.n
-main SharedDeque
-cmd neko shareddeque.n

--next

-neko usemutex.n
-main UseMutex
-cmd neko usemutex.n

The output should resemble:

Message Passing Example
t2 waiting for msg
t1 sending: 0
t2 got: 0
t2 waiting for msg
t1 sending: 1
t2 got: 1
t2 waiting for msg
t1 sending: 2
t2 got: 2
t2 waiting for msg
t1 sending: 3
t2 got: 3
t2 waiting for msg
t1 sending: 4
t2 got: 4
done

Shared Deque Example
t2 waiting for msg
t1 pushing 0
t2 got: 0
t2 waiting for msg
t1 pushing 1
t2 got: 1
t2 waiting for msg
t1 pushing 2
t2 got: 2
t2 waiting for msg
t1 pushing 3
t2 got: 3
t2 waiting for msg
t1 pushing 4
t2 got: 4
done

Shared Object Example
t1 setting 0
t2 got: 0
t2 got: 0
t1 setting 1
t2 got: 1
t2 got: 1
t1 setting 2
t2 got: 2
t1 setting 3
t2 got: 3
t2 got: 3
t1 setting 4
t2 got: 4
done

"t1" refers to thread 1, the thread sending data. "t2" refers to thread 2, which is receiving data.

version #5754, modified 2009-04-14 02:30:49 by ianxm