EduC++ Creating and Managing Threads

Creating and Managing Threads

Prerequisites: Basic understanding of functions, lambdas, and std::ref.
Standard: C++11 introduced std::thread. C++20 added std::jthread with automatic joining and cooperative cancellation via std::stop_token.
void simple_task(int id) {
    std::cout << std::format("Thread {} starting\n", id);
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
    std::cout << std::format("Thread {} finished\n", id);
}

void task_with_result(int id, int& result) {
    result = id * id;
}

int main() {
    std::cout << std::format("Hardware threads: {}\n",
                             std::thread::hardware_concurrency());

1. Basic thread creation

detach() calls std::terminate. Prefer std::jthread (C++20) which
auto-joins.

Watch out: a std::thread that goes out of scope without join() or

std::thread t1(simple_task, 1);
    std::thread t2(simple_task, 2);

    // Must join or detach before destructor!
    t1.join();  // Wait for completion
    t2.join();

2. Thread with reference parameter

wrap references in std::ref(); forgetting this causes a compile
error or silent copy.

Watch out: std::thread copies its arguments by default. You must

int result = 0;
    std::thread t3(task_with_result, 5, std::ref(result));
    t3.join();
    std::cout << std::format("Result: {}\n", result);

3. Lambda threads

running even after main() returns, potentially accessing destroyed
objects. Only detach when the thread is truly self-contained.

Watch out: detach() makes the thread independent -- it continues

std::vector<std::thread> workers;
    for (int i = 0; i < 4; ++i) {
        workers.emplace_back([i] {
            std::cout << std::format("Lambda worker {} running\n", i);
        });
    }

    for (auto& w : workers) {
        w.join();
    }

4. std::jthread -- C++20 auto-joining thread with cooperative

cancellation via stop_token
thread ignores the stop_token it will still block until the
thread finishes naturally.

Watch out: jthread's destructor requests stop AND joins. If your

std::cout << "\n--- std::jthread with stop_token ---\n";
    {
        std::jthread worker([](std::stop_token stoken) {
            int count = 0;
            while (!stoken.stop_requested()) {
                std::cout << std::format("jthread working... (iteration {})\n", ++count);
                std::this_thread::sleep_for(std::chrono::milliseconds(50));
            }
            std::cout << "jthread received stop request, exiting cleanly\n";
        });

        // Let the worker run for a bit
        std::this_thread::sleep_for(std::chrono::milliseconds(180));

        // jthread destructor calls request_stop() then join() automatically
        std::cout << "jthread going out of scope (auto-stop + auto-join)\n";
    }

    std::cout << "All threads completed\n";
    return 0;
}