User Tools

Site Tools


concurrency

Overview

Any function can be run either synchronously (as a new stack frame on the current thread) where the caller waits for the function call to return, or asynchronously (as a new thread) where the caller continues immediately after making the call without waiting for it to return.

Imagine a function:

void printNumbers(int to)
  {
  for (int i = 0; i <= to; i++)
     {
     out.println(iu.intToString(i))
     }
  }

This prints out all of the numbers from 0 up to and including the given parameter. We can call this function synchronously by using:

printNumbers(100)

In this case the caller is blocked until all numbers have been printed. Alternatively we can call this function asynchronously using:

asynch::printNumbers(100)

In this case a new thread is created and the caller continues immediately after initiating the call, proceeding with its own remaining logic at the same time as the list of numbers is being printed.

A function called with asynch:: will return a value of type Thread, irrespective of the actual return value of the function. This Thread value can be used with synchronisation.

Mutual exclusion between multiple threads

In some cases it is necessary to ensure that the logic within two functions (or in multiple instances of the same function) is not interleaved / executed concurrently.

This can be done with the mutex control block which takes any Data type (or the Mutex type, which is a synonym of the Data base type) as a parameter to lock:

Mutex myLock = new Mutex()
 
void functionA()
   {
   mutex(myLock)
      {
      //do something...
      }
   }
 
void functionB()
   {
   mutex(myLock)
      {
      //do something else...
      }
   }

In the above example, the code within functionA() and functionB() will never run at the same time.

Synchronisation between multiple threads

When two threads do something in a “synchronised” way it means that one thread waits for the other thread to do something. There are two main ways to do this in Dana: using wait and signal, or using join.

The join operator is simply used to wait for another thread to finish executing:

Thread t = asynch::printNumbers(100)
t.join()

The above program will now be blocked at t.join() until the printNumbers() function completes.

The wait and signal operators allow for more controlled interaction between two threads. They are typically used when one thread needs to wait for a specific event to occur, where that event will arrive in a different thread:

Thread main;
 
void eventHandler()
   {
   main.signal()
   }
 
int App:main(AppParam params[])
   {
   main = this.thread
   this.thread.wait()
   return 0
   }

In the above code, the main thread of control will halt until the eventHandler() function is called.

The wait and signal operators are actually a simple semaphore which is initialised to zero; the signal operation increments this semaphore by one and the wait operation decrements it by one if it is greater than zero or else blocks the thread if it is zero.

concurrency.txt · Last modified: 2018/02/09 03:59 by barryfp

Page Tools