Home
4Threads & Concurrency
4

Chapter 4 · Final syllabus · Lecture 14

Threads & Concurrency

Why applications use threads, concurrency versus parallelism, Amdahl’s law, user and kernel threads, the many-to-one, one-to-one, many-to-many and two-level models, Pthreads, implicit threading, threading issues and Windows and Linux threads.

← → move between slides · F full screen · M slide list

Before we start

Why this matters

Your phone has eight cores, but a single-threaded program can only ever use one of them.

Threads let one process do several things at once — and, on a multicore CPU, truly in parallel.

This chapter explains how threads are mapped onto the kernel, how much speedup to expect, and what changes when a process has many threads.

Chapter 4 · Lecture 14

Outline

Select a topic to jump to the slide that explains it.

  1. Overview
  2. Multicore programming
  3. Multithreading models
  4. Thread libraries
  5. Implicit threading
  6. Threading issues
  7. Operating system examples

Overview

Motivation

  • Most modern applications are multithreaded
  • Threads run within application
  • Multiple tasks with the application can be implemented by separate threads
    • Update display
    • Fetch data
    • Spell checking
    • Answer a network request
  • Process creation is heavy-weight while thread creation is light-weight
  • Can simplify code, increase efficiency
  • Kernels are generally multithreaded
A word processor might use one thread to show what you type, one to check spelling and one to save in the background — all sharing the same document in memory.

Overview

Single and multithreaded processes

Threads share code, data and files; each has its own registers, program counter and stack.

Overview

Benefits

  • Responsiveness – may allow continued execution if part of process is blocked, especially important for user interfaces
  • Resource sharing – threads share resources of process, easier than shared memory or message passing
  • Economy – cheaper than process creation, thread switching lower overhead than context switching
  • Scalability – process can take advantage of multicore architectures

Multicore programming

Multicore programming

  • Multicore or multiprocessor systems putting pressure on programmers, challenges include:
    • Dividing activities
    • Balance
    • Data splitting
    • Data dependency
    • Testing and debugging
  • Parallelism implies a system can perform more than one task simultaneously
  • Concurrency supports more than one task making progress
    • Single processor / core, scheduler providing concurrency

Multicore programming

Multicore programming (cont.)

Concurrent execution on a single-core system and parallelism on a multi-core system.

Multicore programming

Types of parallelism

  • Types of parallelism
    • Data parallelism – distributes subsets of the same data across multiple cores, same operation on each
    • Task parallelism – distributing threads across cores, each thread performing unique operation

Multicore programming

Types of parallelism (cont.)

Data and task parallelism.

Multicore programming

Amdahl’s law

  • Identifies performance gains from adding additional cores to an application that has both serial and parallel components
  • S is serial portion
  • N processing cores
speedup ≤ 1 / ( S + (1 − S) / N )
  • That is, if application is 75% parallel / 25% serial, moving from 1 to 2 cores results in speedup of 1.6 times
  • As N approaches infinity, speedup approaches 1 / S
Serial portion of an application has disproportionate effect on performance gained by adding additional cores.

Multicore programming

Amdahl’s law (cont.)

Multithreading models

User threads and kernel threads

  • User threads – management done by user-level threads library
    • Three primary thread libraries:
    • POSIX Pthreads
    • Windows threads
    • Java threads
  • Kernel threads – supported by the kernel
    • Examples – virtually all general-purpose operating systems, including: Windows, Linux, Mac OS X, iOS, Android

Multithreading models

User threads and kernel threads (cont.)

User and kernel threads.

Multithreading models

Multithreading models

Many-to-one
One-to-one
Many-to-many

Multithreading models

Many-to-one

  • Many user-level threads mapped to single kernel thread
  • One thread blocking causes all to block
  • Multiple threads may not run in parallel on multicore system because only one may be in kernel at a time
  • Few systems currently use this model
  • Examples:
    • Solaris Green Threads
    • GNU Portable Threads

Multithreading models

Many-to-one (cont.)

Many-to-one model.

Multithreading models

One-to-one

  • Each user-level thread maps to kernel thread
  • Creating a user-level thread creates a kernel thread
  • More concurrency than many-to-one
  • Number of threads per process sometimes restricted due to overhead
  • Examples
    • Windows
    • Linux

Multithreading models

One-to-one (cont.)

One-to-one model.

Multithreading models

Many-to-many model

  • Allows many user level threads to be mapped to many kernel threads
  • Allows the operating system to create a sufficient number of kernel threads
  • Windows with the ThreadFiber package
  • Otherwise not very common
Many-to-many model.

Multithreading models

Two-level model

  • Similar to M:M, except that it allows a user thread to be bound to kernel thread
Two-level model.

Interactive

Compare the models

Thread libraries

Thread libraries

  • Thread library provides programmer with API for creating and managing threads
  • Two primary ways of implementing
    • Library entirely in user space
    • Kernel-level library supported by the OS

Thread libraries

Pthreads

  • May be provided either as user-level or kernel-level
  • A POSIX standard (IEEE 1003.1c) API for thread creation and synchronization
  • Specification, not implementation
  • API specifies behavior of the thread library, implementation is up to development of the library
  • Common in UNIX operating systems (Linux & Mac OS X)
#include <pthread.h>
#include <stdio.h>

void *runner(void *param)
{
    printf("hello from a thread\n");
    pthread_exit(0);
}

int main()
{
    pthread_t tid;
    pthread_create(&tid, NULL, runner, NULL);
    pthread_join(tid, NULL);
    return 0;
}

Code and output

Pthreads: run it

threadsum.c — the textbook’s summation thread
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>

int sum;                          /* shared by the threads */
void *runner(void *param);        /* the thread's function */

int main(int argc, char *argv[])
{
    pthread_t tid;                /* thread identifier */
    pthread_attr_t attr;          /* thread attributes */

    pthread_attr_init(&attr);
    pthread_create(&tid, &attr, runner, "10");
    pthread_join(tid, NULL);      /* wait for the thread to exit */
    printf("sum = %d\n", sum);
    return 0;

Code and output

Pthreads: run it (cont.)

}

void *runner(void *param)
{
    int upper = atoi(param);
    sum = 0;
    for (int i = 1; i <= upper; i++)
        sum += i;
    pthread_exit(0);
}
Output
Terminal
$ gcc threadsum.c -o threadsum -pthread && ./threadsum
sum = 55

main() creates one thread that sums 1 … 10 into the shared variable sum, then waits for it with pthread_join() before printing.

Implicit threading

Implicit threading

  • Growing in popularity as numbers of threads increase, program correctness more difficult with explicit threads
  • Creation and management of threads done by compilers and run-time libraries rather than programmers
  • Five methods explored
    • Thread pools
    • Fork-join
    • OpenMP
    • Grand Central Dispatch
    • Intel Threading Building Blocks

Implicit threading

Thread pools

  • Create a number of threads in a pool where they await work
  • Advantages:
    • Usually slightly faster to service a request with an existing thread than create a new thread
    • Allows the number of threads in the application(s) to be bound to the size of the pool
    • Separating task to be performed from mechanics of creating task allows different strategies for running task
      • i.e., tasks could be scheduled to run periodically

Windows API supports thread pools:

DWORD WINAPI PoolFunction(PVOID Param) {
   /*
    * this function runs as a separate thread.
    */
}

Implicit threading

Fork-join parallelism

Multiple threads (tasks) are forked, and then joined.

Fork-join parallelism.

Implicit threading

Fork-join parallelism (cont.)

General algorithm for fork-join strategy:

Task(problem)
   if problem is small enough
      solve the problem directly
   else
      subtask1 = fork(new Task(subset of problem))
      subtask2 = fork(new Task(subset of problem))

      result1 = join(subtask1)
      result2 = join(subtask2)

      return combined results

Implicit threading

Fork-join parallelism (cont.)

Fork-join in Java: recursively summing an array.

Implicit threading

OpenMP

  • Set of compiler directives and an API for C, C++, FORTRAN
  • Provides support for parallel programming in shared-memory environments
  • Identifies parallel regions – blocks of code that can run in parallel
  • #pragma omp parallel
  • Create as many threads as there are cores

Run the for loop in parallel:

#pragma omp parallel for
for (i = 0; i < N; i++) {
   c[i] = a[i] + b[i];
}
#include <omp.h>
#include <stdio.h>

int main(int argc, char *argv[])
{
   /* sequential code */

   #pragma omp parallel
   {
      printf("I am a parallel region.");
   }

   /* sequential code */

   return 0;
}

Code and output

OpenMP: run it

omp.c
#include <omp.h>
#include <stdio.h>

int main(int argc, char *argv[])
{
    /* sequential code */

    #pragma omp parallel        /* one thread per core */
    {
        printf("I am a parallel region.\n");
    }

    /* sequential code */
    return 0;
}
Output
Terminal · sample run on Linux
$ gcc -fopenmp omp.c -o omp && ./omp
I am a parallel region.
I am a parallel region.
I am a parallel region.
I am a parallel region.

On a 4-core machine OpenMP creates 4 threads for the parallel region, so the line is printed 4 times.

Implicit threading

Grand Central Dispatch

  • Apple technology for macOS and iOS operating systems
  • Extensions to C, C++ and Objective-C languages, API, and run-time library
  • Allows identification of parallel sections
  • Manages most of the details of threading
  • Block is in “^{ }”: ^{ printf("I am a block"); }
  • Blocks placed in dispatch queue
    • Assigned to available thread in thread pool when removed from queue
  • Two types of dispatch queues:
    • serial – blocks removed in FIFO order, queue is per process, called main queue
      • Programmers can create additional serial queues within program
    • concurrent – removed in FIFO order but several may be removed at a time
      • Four system wide queues divided by quality of service: QOS_CLASS_USER_INTERACTIVE, QOS_CLASS_USER_INITIATED, QOS_CLASS_USER_UTILITY, QOS_CLASS_USER_BACKGROUND

Implicit threading

Intel Threading Building Blocks (TBB)

  • Template library for designing parallel C++ programs

A serial version of a simple for loop:

for (int i = 0; i < n; i++) {
   apply(v[i]);
}

The same for loop written using TBB with parallel_for statement:

parallel_for (size_t(0), n, [=](size_t i) {apply(v[i]);});

Threading issues

Threading issues

  • Semantics of fork() and exec() system calls
  • Signal handling
    • Synchronous and asynchronous
  • Thread cancellation of target thread
    • Asynchronous or deferred
  • Thread-local storage
  • Scheduler activations

Threading issues

Semantics of fork( ) and exec( )

  • Does fork() duplicate only the calling thread or all threads?
    • Some UNIXes have two versions of fork
  • exec() usually works as normal – replace the running process including all threads
If a thread calls exec() immediately after fork(), duplicating all threads is wasted work, because exec() replaces them all anyway.

Threading issues

Signal handling

  • Signals are used in UNIX systems to notify a process that a particular event has occurred.
  • A signal handler is used to process signals
    • Signal is generated by particular event
    • Signal is delivered to a process
    • Signal is handled by one of two signal handlers:
      • default
      • user-defined

Threading issues

Signal handling (cont.)

  • Every signal has default handler that kernel runs when handling signal
    • User-defined signal handler can override default
    • For single-threaded, signal delivered to process
  • Where should a signal be delivered for multi-threaded?
    • Deliver the signal to the thread to which the signal applies
    • Deliver the signal to every thread in the process
    • Deliver the signal to certain threads in the process
    • Assign a specific thread to receive all signals for the process

Threading issues

Thread cancellation

  • Terminating a thread before it has finished
  • Thread to be canceled is target thread
  • Two general approaches:
    • Asynchronous cancellation terminates the target thread immediately
    • Deferred cancellation allows the target thread to periodically check if it should be cancelled

Pthread code to create and cancel a thread:

pthread_t tid;

/* create the thread */
pthread_create(&tid, 0, worker, NULL);

. . .

/* cancel the thread */
pthread_cancel(tid);

/* wait for the thread to terminate */
pthread_join(tid,NULL);

Operating system examples

Operating system examples

Windows threads

one-to-one, kernel-level; ETHREAD, KTHREAD and TEB

Linux threads

called tasks; created with clone()

Operating system examples

Windows threads

  • Windows API – primary API for Windows applications
  • Implements the one-to-one mapping, kernel-level
  • Each thread contains
    • A thread id
    • Register set representing state of processor
    • Separate user and kernel stacks for when thread runs in user mode or kernel mode
    • Private data storage area used by run-time libraries and dynamic link libraries (DLLs)
  • The register set, stacks, and private storage area are known as the context of the thread
  • The primary data structures of a thread include:
    • ETHREAD (executive thread block) – includes pointer to process to which thread belongs and to KTHREAD, in kernel space
    • KTHREAD (kernel thread block) – scheduling and synchronization info, kernel-mode stack, pointer to TEB, in kernel space
    • TEB (thread environment block) – thread id, user-mode stack, thread-local storage, in user space

Operating system examples

Windows threads (cont.)

Windows threads data structures.

Operating system examples

Linux threads

  • Linux refers to them as tasks rather than threads
  • Thread creation is done through clone() system call
  • clone() allows a child task to share the address space of the parent task (process)
    • Flags control behavior
  • struct task_struct points to process data structures (shared or unique)
flagmeaning
CLONE_FSFile-system information is shared.
CLONE_VMThe same memory space is shared.
CLONE_SIGHANDSignal handlers are shared.
CLONE_FILESThe set of open files is shared.

Worked example 1 of 2

Worked example 1

An application is 80% parallel. What speedup does Amdahl’s law give on 4 cores and on 8 cores? What is the limit?
Show the solution
S = 0.2: N = 4 → 1 / (0.2 + 0.8/4) = 2.5×; N = 8 → 1 / (0.2 + 0.8/8) = 3.33×; limit = 1 / 0.2 = 5×

Doubling the cores from 4 to 8 gives only 33% more speed; the serial 20% dominates.

Worked example 2 of 2

Worked example 2

Four user threads run on the many-to-one model and thread T2 makes a blocking read(). Which threads can run? What changes with one-to-one?
Show the solution

Many-to-one: the single kernel thread blocks in the system call, so all four user threads are blocked until the read completes.

One-to-one: only T2’s kernel thread blocks; T1, T3 and T4 keep running, and on a multicore system several can run in parallel.

Practice

Practice (1 of 2)

Try each one on paper first, then check your answer.

1. Give the four benefits of multithreading.
Show the answer
Responsiveness, resource sharing, economy and scalability.
2. Distinguish concurrency from parallelism.
Show the answer
Concurrency: more than one task makes progress (possible on one core by interleaving). Parallelism: more than one task runs at the same instant, which needs multiple cores.
3. Using Amdahl’s law, show that 75% parallel code on 2 cores gives a speedup of 1.6.
Show the answer
1 / (0.25 + 0.75 / 2) = 1 / 0.625 = 1.6.

Practice

Practice (2 of 2)

Try each one on paper first, then check your answer.

4. Which model allows a user thread to be bound to a kernel thread?
Show the answer
The two-level model (a variation of many-to-many).
5. Compare asynchronous and deferred cancellation.
Show the answer
Asynchronous cancellation terminates the target thread immediately; deferred cancellation lets the target check periodically whether it should be cancelled, so it can stop at a safe point.
6. What does CLONE_VM do in Linux clone()?
Show the answer
The child task shares the same memory space as the parent, which makes it behave like a thread.

Check yourself

Quiz

Summary · End of Chapter 4

Summary

  • A thread is a unit of CPU use with its own registers, PC and stack, sharing code, data and files with the other threads of its process.
  • Benefits: responsiveness, resource sharing, economy, scalability; concurrency is progress, parallelism is simultaneous execution.
  • Amdahl’s law: speedup ≤ 1 / (S + (1 − S)/N), limited to 1/S by the serial part.
  • User threads map to kernel threads many-to-one, one-to-one (Windows, Linux), many-to-many or two-level.
  • Libraries (Pthreads, Windows, Java) and implicit threading (thread pools, fork-join, OpenMP, GCD, TBB) create and manage threads.
  • Issues: fork/exec semantics, signal delivery, asynchronous vs deferred cancellation; Windows uses ETHREAD/KTHREAD/TEB and Linux uses clone() tasks.