Shiklo Tools
PDF40
Image20
Calculators & Converters25
Text & Writing13
Developer26
Video14
Audio14
Document14
Data & Spreadsheet12
Web & SEO9
OCR & Scanning5
Archive & File12
Security & Privacy23
Health & Medical29
QR & Barcode5

C++ Threads

A thread is a separate line of work inside one program. Several threads can run at the same time on different processor cores, which makes long calculations faster and keeps programs responsive. In the browser, the editor reloads the page in a special threads mode the first time a program uses threads; your files are kept.

Starting and joining threads

#include <iostream>
#include <thread>
#include <vector>

void work(int id, long long& result) {
    long long sum = 0;
    for (int i = 1; i <= 1000; i++) sum += i * id;
    result = sum;
}

int main() {
    std::vector<long long> results(3);
    std::vector<std::thread> threads;
    for (int id = 0; id < 3; id++) {
        threads.emplace_back(work, id + 1, std::ref(results[id]));
    }
    for (auto& t : threads) t.join();          // wait for every thread
    for (long long r : results) std::cout << r << "\n";
    return 0;
}
Output
500500
1001000
1501500

std::thread starts a function in a new thread; its arguments follow. std::ref passes a reference (threads copy their arguments otherwise). join waits until a thread has finished; every thread must be joined (or detached) before its std::thread object goes away, or the program stops with an error. C++20's std::jthread joins by itself.

Each thread above writes to its own element of results, so the threads never touch the same data.

Shared data and mutex

When several threads change the same variable, changes can be lost: this is a race condition, and it makes results differ from run to run. A std::mutex lets only one thread at a time into the protected code; std::lock_guard locks it and unlocks it automatically at the end of the block.

#include <iostream>
#include <mutex>
#include <thread>
#include <vector>

std::mutex m;
long long total = 0;

void addRange(int from, int to) {
    long long local = 0;
    for (int i = from; i <= to; i++) local += i;
    std::lock_guard<std::mutex> lock(m);      // one thread at a time
    total += local;
}

int main() {
    std::vector<std::thread> workers;
    for (int t = 0; t < 4; t++) {
        workers.emplace_back(addRange, t * 250000 + 1, (t + 1) * 250000);
    }
    for (auto& w : workers) w.join();
    std::cout << "Sum of 1 to 1000000 = " << total << "\n";
    return 0;
}
Output
Sum of 1 to 1000000 = 500000500000

Each thread adds its numbers in a local variable first and locks only once for the final addition; locking for every single number would be correct but slow.

Atomic counters

For a single counter, std::atomic is simpler than a mutex:

#include <atomic>
#include <iostream>
#include <thread>
#include <vector>

int main() {
    std::atomic<int> count{0};
    std::vector<std::thread> t;
    for (int i = 0; i < 4; i++) {
        t.emplace_back([&count] { for (int k = 0; k < 10000; k++) count++; });
    }
    for (auto& th : t) th.join();
    std::cout << count << "\n";
    return 0;
}
Output
40000

async and future

std::async runs a function, possibly in another thread, and returns a std::future from which get() collects the result. It is the easiest way to compute several results in parallel:

#include <future>
#include <iostream>

long long sumTo(long long n) {
    long long s = 0;
    for (long long i = 1; i <= n; i++) s += i;
    return s;
}

int main() {
    auto a = std::async(std::launch::async, sumTo, 1000);
    auto b = std::async(std::launch::async, sumTo, 2000);
    std::cout << a.get() + b.get() << "\n";
    return 0;
}
Output
2501500

Practice exercises

  1. Remove the lock_guard from the mutex example, run it several times and compare the results.
  2. Use std::async to find the largest element of each half of a vector in parallel.
  3. Count the prime numbers below 100000 with four threads, each checking a quarter of the range.