11 - Concurrency and Threads

Class: CSCE-313


Notes:

Outline:

  1. What is concurrency?
    • Macro illusion that multiple things are happening at the same time
  2. What is a thread?
    • A useful abstraction for a program to program in a concurrent world
  3. Thread context switch
  4. Thread API

What is concurrency?

Concurrency is the execution of a set of multiple instruction
streams at the "same" time. This occurs when there are several
processes (or threads) executing in parallel.

image-82.png323

Notes:

What is a thread?

image-83.png182

Notes:

Threads as a programmatic framework

image-84.png414

Notes:

Multi-threaded process

image-85.png212

Notes:

Process vs. Thread

Each thread has its own:

Threads share

image-86.png311

Notes:

Motivation for Threads

Basically two reasons:

  1. Processes are expensive to create
  2. It takes time to switch between processes
    • May suffer cache and TLB misses on context switch
  3. Communication between processes may need to be done through external structures such as files, pipes, etc
  4. Synchronization between processes may be cumbersome

Notes:

Process Context Switch

image-87.png312

Notes:

Thread Context Switch

image-92.png310x431

Notes:

Thread-based applications

Manager/worker: a single manager thread assigns work to other threads, the workers. The manager handles all input and assigns tasks to worker threads.

Pipeline: A task is split into sub-operations, which are handled in series, but concurrently, by a different thread. For e.g., an automobile assembly line.

Notes:

Threaded web server

image-88.png296

Notes:

Thread Execution

Creating a thread is like calling a function directly, with a slight difference shown below:

Notes:

Problem with shared addresses-I

image-89.png273

Notes:

The race condition problem

Race condition: the output of a concurrent program depends on the order of operations between threads.

We cannot make any assumption about the relative speed of threads
Non-determinism is omnipresent:

Notes:

Rate condition in Assembly

image-90.png

Notes:

Race condition may happen because of instruction reordering

Most architectures support this feature:

image-91.png553

Notes:

Out-of-Order Execution causing Race Condition

00 - TAMU Brain/6th Semester (Spring 26)/CSCE-313/Lecture/Visual Aids/image-26.png502

Notes:

pthread (POSIX thread) creation

With pthreads, when a program runs, it also starts out as a single process with a single thread of control.

The pthread_create() function starts a new thread in the calling process.

#include <pthread.h>
int pthread_create(pthread_t *tid, const pthread_attr_t *attr,
    void *(*func) (void *), void *arg);

pthread_create returns 0 if successful, a positive error code if not.

C++ Thread API

#include <iostream>
#include <thread>
#include <unistd.h>
using namespace std;

void foo() {
    sleep(3);
    cout << "foo done" << endl;
}
void bar(int x) {
    sleep(1);
    cout << "bar done" << endl;
}

int main() {
    thread foothrd(foo); // calls foo() in a thread
    thread barthrd(bar, 77); // calls bar(x) in a thread
    cout << "main, foo and bar would now execute concurrently..." << endl;
    
    foothrd.join(); // pauses until foo finishes
    barthrd.join(); // pauses until bar finishes
    cout << "foo and bar completed." << endl;
    return 0;
}

Output:

maon, foo, bar, fib execute concurrently ...
bar: received 77
foo: done
foo, bar, fib completed

Race Condition Demonstration

#include <iostream>
#include <thread>
#include <unistd.h>
using namespace std;

void func(int *p, int x) {
    // increment *p x times
    for (int i = 0; i < x; i++)
    *p = *p + 1;
}

int main(int ac, char **av) {
    int data = 0;
    int times = atoi(av[1]);
    // start 2 threads to increment
    thread t1(func, &data, times);
    thread t2(func, &data, times);
    t1.join();
    t2.join();
    cout << "data=" << data << endl;
    return 0;
}
root@ubuntu-csce313:~\# g++ race-condition.cc root@ubuntu-csce313:~\# ./a.out 100000 data=200000
root@ubuntu-csce313:~\# ./a.out 1000000 data=2000000
root@ubuntu-csce313:~\# ./a.out 10000000 data=10938339
root@ubuntu-csce313:~\# ./a.out 10000000 data=14035068
root@ubuntu-csce313:~\# ./a.out 10000000 data=17764839
root@ubuntu-csce313:~\# ./a.out 10000000 data=16256394
root@ubuntu-csce313:~\# ./a.out 10000000 data=14049964

Thread-safe functions

A function is said to be thread-safe if it can safely be invoked by multiple threads at the same time; put conversely, if a function is not thread-safe, then we can't call it from one thread while it is being executed in another thread. [Kerrisk 31.1]

Not all functions can be called from threads

Safe functions

Unsafe functions