Post

QA - Section 1.02. Multithreading & Concurrency

QA - Section 1.02. Multithreading & Concurrency

QA - Section 1.02. Multithreading & Concurrency


1. Core vs Thread

A core is a physical hardware component inside the CPU that executes instructions. A thread is a sequence of instructions that can be scheduled and executed by the CPU.

When we say a thread is “waiting,” it does not mean the program has stopped running; rather, it means that the CPU is not actively performing useful computation for that task. For example, during an I/O operation such as reading a file or waiting for a network response, the program issues a request to the operating system and then pauses until the result is ready. During this time, the thread is still considered active, but it is blocked, and the CPU is effectively idle with respect to that task. This is why waiting is important in performance discussions: if a program uses only a single thread, the CPU may spend significant time doing nothing while waiting for external operations to complete. By introducing multiple threads, the system can schedule other work while one thread is waiting, thereby improving overall efficiency and utilization of the CPU. In this process, context switching cost affect to performance.


2. Process vs Thread

  • Process: Independent memory space
  • Thread: Shares memory within a process
AspectProcessThread
MemorySeparateShared
Creation CostHighLow
CommunicationIPC requiredDirect (shared memory)
StabilityHighLower (race conditions possible)

C++ Example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <thread>
#include <iostream>

void work() {
    std::cout << "Thread running\n";
}

int main() {
    std::thread t1(work);
    std::thread t2(work);

    t1.join();
    t2.join();
}
1
2
3
4
Process
 ├── Main Thread (main 실행)
 ├── Thread t1 (work 실행)
 └── Thread t2 (work 실행)

3. Concurrency vs Parallelism

  • Concurrency: Tasks make progress together (not necessarily at the same time)
  • Parallelism: Tasks run at the same time (multi-core)

  • Single-core → concurrency only
  • Multi-core → parallelism possible

C++ Example

1
2
3
4
5
6
#include <future>

auto f = std::async(std::launch::async, [] 
{
    // concurrent execution
});

It is request of concurrency and whether parallelism or not depends on how many core we use. When single-core, in this case, not parallelism. And if multi-core, it is parallelsim.


4. Task Parallelism vs Data Parallelism

Main difference of these parallelisms are same task or not.

Task Parallelism

Different tasks run in parallel

1
2
std::thread t1([] { loadImage(); });
std::thread t2([] { processImage(); });
Data Parallelism

Same task on different data

1
2
3
4
5
6
7
void process(int* arr, int start, int end) {
    for (int i = start; i < end; i++)
        arr[i] *= 2;
}

std::thread t1(process, arr, 0, 500);
std::thread t2(process, arr, 500, 1000);

5. CPU-bound vs IO-bound

CPU-bound is busy from CPU computation, but IO-bound is not busy from CPU computation but busy from I/O

CPU-bound
  • Limited by computation
  • Benefits from multithreading, SIMD
1
2
for (int i = 0; i < 1e9; i++) 
    x += i;
IO-bound
  • Limited by I/O (disk, network)
  • Benefits from multithreading
1
2
3
4
5
6
#include <fstream>
#include <string>

std::ifstream file("big.txt");
std::string line;
while (getline(file, line)) {}

6. Context Switching Cost

The overhead of switching CPU execution between threads

  • Register save/restore
  • Cache invalidation
  • Pipeline flush
Bad Example
1
2
for (int i = 0; i < 10000; i++) 
    std::thread([]{}).detach();

Too many threads can degrade performance


7. What is Thread-safe

Code that behaves correctly when accessed by multiple threads

Problem: Race Condition
Unsafe
1
2
3
4
int counter = 0;

void inc() 
    counter++; // race condition
Using Mutex
1
2
3
4
5
6
7
8
#include <mutex>

int counter = 0;
std::mutex m;

void inc() 
    std::lock_guard<std::mutex> lock(m);
    counter++;
Using Atomic
1
2
3
4
5
6
#include <atomic>

std::atomic<int> counter = 0;

void inc() 
    counter++;

8. Reentrant vs Thread-safe

Thread-safe
  • Allows shared state
  • Uses synchronization (mutex, atomic)
1
2
3
4
5
6
7
8
std::mutex m;
int counter = 0;

void func() 
{
    std::lock_guard<std::mutex> lock(m);
    counter++;
}
Reentrant
  • No shared state
  • Safe even if interrupted and re-entered
1
2
3
4
int func(int x) 
{
    return x * x;
}
AspectThread-safeReentrant
Shared StateAllowed (with protection)Not allowed
Interrupt-safeNoYes
This post is licensed under CC BY 4.0 by the author.