...

/

Tasks and Worker Processes

Tasks and Worker Processes

This lesson lists the possible ways of communication and their pros and cons in different situations.

Suppose we have to perform several tasks; a task is performed by a worker (process). A Task can be defined as a struct (the concrete details are not important here):

type Task struct {
  // some state
}

1st paradigm: use shared memory to synchronize

The pool of tasks is shared memory. To synchronize the work and to avoid race conditions, we have to guard the pool with a Mutex lock:

type Pool struct {
  Mu sync.Mutex
  Tasks []Task
}

A sync.Mutex (as mentioned earlier in the course) is a mutual exclusion lock. It serves to guard the entrance to a critical section in code. Only one goroutine (thread) can enter that section at one time. If more than one goroutine is allowed, a race-condition can exist, which means the Pool struct can no longer be updated correctly.

In the traditional model (applied in most classic OO-languages like C++, Java, and C#) the Worker process could be coded as:

func Worker(pool *Pool) {
  for {
    pool.Mu.Lock()
    // begin critical section:
    task := pool.Tasks[0] // take the first task
    pool.Tasks = pool.Tasks[1:] // update the pool of tasks
    // end critical section
    pool.Mu.Unlock()
   
...