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.
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
Overview
Single and multithreaded processes
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.)
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.)
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
- 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
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.)
Multithreading models
Multithreading models
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.)
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.)
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
Multithreading models
Two-level model
- Similar to M:M, except that it allows a user thread to be bound to kernel thread
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
#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);
}$ 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.
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.)
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
#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;
}$ 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
- Four system wide queues divided by quality of service:
- serial – blocks removed in FIFO order, queue is per process, called main queue
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()andexec()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
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 TEBLinux 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.)
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_structpoints to process data structures (shared or unique)
| flag | meaning |
|---|---|
CLONE_FS | File-system information is shared. |
CLONE_VM | The same memory space is shared. |
CLONE_SIGHAND | Signal handlers are shared. |
CLONE_FILES | The set of open files is shared. |
Worked example 1 of 2
Worked example 1
Show the solution
Doubling the cores from 4 to 8 gives only 33% more speed; the serial 20% dominates.
Worked example 2 of 2
Worked example 2
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.
Show the answer
Show the answer
Show the answer
Practice
Practice (2 of 2)
Try each one on paper first, then check your answer.
Show the answer
Show the answer
Show the answer
Check yourself
Quiz
Resources
Further learning
Free material to go deeper. Links open in a new tab.
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.