Language
日本語
English

Caution

JavaScript is disabled in your browser.
This site uses JavaScript for features such as search.
For the best experience, please enable JavaScript before browsing this site.

  1. Home
  2. C++ Dictionary
  3. threads

threads

In C++, you can create threads using the std::thread class from the <thread> header. Threads allow multiple operations to run concurrently, so you can continue other work while waiting for a heavy computation or I/O. When sharing data between threads, use std::mutex or std::lock_guard for mutual exclusion to prevent data races.

Syntax

// ========================================
// std::thread basic syntax
// ========================================

#include <thread>
#include <mutex>

// ----------------------------------------
// Creating a thread and joining
// ----------------------------------------

// Launch a function as a thread
void worker() { /* work */ }
std::thread t(worker);  // start the thread
t.join();               // wait for completion (caller blocks)

// Launch a thread with a lambda (C++11 and later)
std::thread t2([]() { /* work */ });
t2.join();

// Launch a thread with arguments
void workerWithArg(int id, std::string name) { /* work */ }
std::thread t3(workerWithArg, 1, "Okabe");
t3.join();

// ----------------------------------------
// detach: separate the thread to run independently
// ----------------------------------------
std::thread t4(worker);
t4.detach();  // if not joining, detach is required

// ----------------------------------------
// Mutual exclusion with mutex
// ----------------------------------------
std::mutex mtx;

void safeWorker() {
    std::lock_guard<std::mutex> lock(mtx);  // released automatically when scope ends
    // critical section (access to shared data)
}

// ----------------------------------------
// Thread identification
// ----------------------------------------
std::thread::id tid = t.get_id();  // get thread ID
// std::this_thread::get_id()      // get current thread's ID

Syntax Reference

Syntax / ClassDescription
std::thread t(f)Launches function f on a new thread. Additional arguments can be passed after f.
t.join()Waits for thread t to finish. The calling thread blocks. If the destructor is called without join or detach, std::terminate is called.
t.detach()Runs the thread independently in the background. After detaching, join can no longer be called.
t.joinable()Returns whether the thread is in a state where join or detach can be called.
std::mutexA mutual exclusion lock. Use lock() / unlock() for exclusive access.
std::lock_guardA RAII wrapper that locks a mutex in its constructor and automatically releases it in its destructor.
std::unique_lockA more flexible mutex wrapper than lock_guard. Supports releasing and re-acquiring the lock partway through.
std::this_thread::sleep_for()Puts the current thread to sleep for a specified duration.
std::this_thread::get_id()Returns the ID of the current thread.
hardware_concurrency()Returns the number of logical cores. Useful as a reference when deciding how many threads to create.

Sample Code

sg_threads.cpp
// ========================================
// sg_threads.cpp
// Each lab member from Steins;Gate handles
// a time-machine development task concurrently.
// Demonstrates std::thread / std::mutex / std::lock_guard.
// ========================================

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

// ========================================
// Shared log buffer and its protecting mutex.
// Without a mutex, simultaneous writes to cout
// from multiple threads would produce garbled output.
// ========================================
std::mutex logMtx;

void log(const std::string& member, const std::string& task) {
    // lock_guard releases the lock automatically when the scope ends
    std::lock_guard<std::mutex> lock(logMtx);
    std::cout << "[" << member << "] " << task << std::endl;
}

// ========================================
// Okabe Rintaro: handles IBN 5100 analysis
// ========================================
void okabe(int stepCount) {
    log("Okabe Rintaro", "Starting IBN 5100 analysis...");
    for (int i = 1; i <= stepCount; i++) {
        // simulate heavy work with sleep
        std::this_thread::sleep_for(std::chrono::milliseconds(120));
        log("Okabe Rintaro", "Analysis step " + std::to_string(i) + " done. El Psy Kongroo.");
    }
    log("Okabe Rintaro", "IBN 5100 analysis complete. Is this the Steins Gate?");
}

// ========================================
// Makise Kurisu: handles time-leap machine theory
// ========================================
void makise(int stepCount) {
    log("Makise Kurisu", "Starting time-leap machine theory construction...");
    for (int i = 1; i <= stepCount; i++) {
        std::this_thread::sleep_for(std::chrono::milliseconds(80));
        log("Makise Kurisu", "Theory step " + std::to_string(i) + " done. Analyzing memory transfer mechanism.");
    }
    log("Makise Kurisu", "Time-leap theory complete. I couldn't have reached this conclusion without you.");
}

// ========================================
// Shiina Mayuri: handles supply management (Operation Tools)
// ========================================
void mayuri(int stepCount) {
    log("Shiina Mayuri", "Starting supply management~!");
    for (int i = 1; i <= stepCount; i++) {
        std::this_thread::sleep_for(std::chrono::milliseconds(200));
        log("Shiina Mayuri", "Supply check " + std::to_string(i) + " done. Delivered Pocky to Christina!");
    }
    log("Shiina Mayuri", "Supply management complete. Mayushii did her best~!");
}

// ========================================
// Amane Suzuha: handles time-machine assembly
// ========================================
void suzuha(int stepCount) {
    log("Amane Suzuha", "Starting time-machine assembly.");
    for (int i = 1; i <= stepCount; i++) {
        std::this_thread::sleep_for(std::chrono::milliseconds(150));
        log("Amane Suzuha", "Assembly step " + std::to_string(i) + " done. Hurrying to change the future of 2036.");
    }
    log("Amane Suzuha", "Time-machine assembly complete. Only fuel remains.");
}

// ========================================
// Hashida Itaru: handles divergence meter verification
// ========================================
void daru(int stepCount) {
    log("Hashida Itaru", "Starting divergence meter verification (full moe-code mode).");
    for (int i = 1; i <= stepCount; i++) {
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
        log("Hashida Itaru", "Verification step " + std::to_string(i) + " done. Showing off my true hacking skills.");
    }
    log("Hashida Itaru", "Divergence meter verification complete. World line divergence confirmed: 1.048596.");
}

// ========================================
// Main: launch each lab member as a thread
// ========================================
int main() {
    std::cout << "=== Future Gadget Lab Time-Machine Development Project ===" << std::endl;
    std::cout << "Logical cores: " << std::thread::hardware_concurrency() << std::endl;
    std::cout << std::endl;

    // number of steps per task
    const int STEPS = 3;

    // launch each lab member as a separate thread
    std::thread t_okabe(okabe,  STEPS);
    std::thread t_makise(makise, STEPS);
    std::thread t_mayuri(mayuri, STEPS);
    std::thread t_suzuha(suzuha, STEPS);
    std::thread t_daru(daru,   STEPS);

    // wait for all threads to finish
    // without join(), std::terminate will be triggered
    t_okabe.join();
    t_makise.join();
    t_mayuri.join();
    t_suzuha.join();
    t_daru.join();

    std::cout << std::endl;
    std::cout << "=== All tasks complete. Operation Steins Gate is ready to launch. ===" << std::endl;

    return 0;
}
# Compile (link the thread library with -lpthread)
g++ -std=c++11 sg_threads.cpp -o sg_threads -lpthread && ./sg_threads
=== Future Gadget Lab Time-Machine Development Project ===
Logical cores: 8

[Okabe Rintaro] Starting IBN 5100 analysis...
[Makise Kurisu] Starting time-leap machine theory construction...
[Shiina Mayuri] Starting supply management~!
[Amane Suzuha] Starting time-machine assembly.
[Hashida Itaru] Starting divergence meter verification (full moe-code mode).
[Makise Kurisu] Theory step 1 done. Analyzing memory transfer mechanism.
[Hashida Itaru] Verification step 1 done. Showing off my true hacking skills.
[Okabe Rintaro] Analysis step 1 done. El Psy Kongroo.
[Amane Suzuha] Assembly step 1 done. Hurrying to change the future of 2036.
[Makise Kurisu] Theory step 2 done. Analyzing memory transfer mechanism.
[Hashida Itaru] Verification step 2 done. Showing off my true hacking skills.
[Okabe Rintaro] Analysis step 2 done. El Psy Kongroo.
[Makise Kurisu] Theory step 3 done. Analyzing memory transfer mechanism.
[Makise Kurisu] Time-leap theory complete. I couldn't have reached this conclusion without you.
[Amane Suzuha] Assembly step 2 done. Hurrying to change the future of 2036.
[Hashida Itaru] Verification step 3 done. Showing off my true hacking skills.
[Hashida Itaru] Divergence meter verification complete. World line divergence confirmed: 1.048596.
[Okabe Rintaro] Analysis step 3 done. El Psy Kongroo.
[Okabe Rintaro] IBN 5100 analysis complete. Is this the Steins Gate?
[Amane Suzuha] Assembly step 3 done. Hurrying to change the future of 2036.
[Amane Suzuha] Time-machine assembly complete. Only fuel remains.
[Shiina Mayuri] Supply check 1 done. Delivered Pocky to Christina!
[Shiina Mayuri] Supply check 2 done. Delivered Pocky to Christina!
[Shiina Mayuri] Supply check 3 done. Delivered Pocky to Christina!
[Shiina Mayuri] Supply management complete. Mayushii did her best~!

=== All tasks complete. Operation Steins Gate is ready to launch. ===

Common Mistakes

In thread programming, forgetting join() and causing data races are among the most common mistakes.

Forgetting join() causes abnormal program termination

The destructor of a std::thread object calls std::terminate() if the thread is still joinable (neither joined nor detached). Be careful to ensure join is called even when a function returns early or an exception is thrown.

NG
#include <iostream>
#include <thread>
#include <stdexcept>

void worker() {
    std::this_thread::sleep_for(std::chrono::milliseconds(50));
    std::cout << "Hashida Itaru: work done" << std::endl;
}

// NG: forgetting join() will crash with std::terminate()
void badExample() {
    std::thread t(worker);
    // if an exception is thrown or an early return occurs,
    // t.join() is never reached and the destructor calls terminate
    throw std::runtime_error("error occurred");
    t.join();  // never reached
}
OK
// OK: use try-catch or a RAII wrapper to guarantee join() is called
void goodExample() {
    std::thread t(worker);
    try {
        // some work
    } catch (...) {
        t.join();  // join even when an exception is thrown
        throw;
    }
    t.join();  // join on normal exit too
}

Data races cause undefined behavior

When multiple threads read and write the same variable without mutual exclusion, a data race occurs. A data race is undefined behavior in C++ and can manifest as crashes, corrupted values, or intermittent bugs. Always protect shared data with std::mutex and std::lock_guard.

NG: no mutex (data race)
#include <iostream>
#include <thread>
#include <mutex>

int sharedCounter = 0;  // shared variable
std::mutex counterMtx;

// NG: reading and writing from multiple threads without a mutex causes a data race
void badIncrement() {
    for (int i = 0; i < 1000; i++) {
        sharedCounter++;  // read-increment-write is not atomic
    }
}

int main() {
    // NG example (result may not be 2000)
    sharedCounter = 0;
    std::thread t1(badIncrement);
    std::thread t2(badIncrement);
    t1.join();
    t2.join();
    std::cout << "NG: " << sharedCounter << " (expected: 2000)" << std::endl;
    return 0;
}
OK: protected with mutex (safe)
// OK: protecting with a mutex makes the increment safe
void safeIncrement() {
    for (int i = 0; i < 1000; i++) {
        std::lock_guard<std::mutex> lock(counterMtx);
        sharedCounter++;  // no other thread can enter while locked
    }
}

int main() {
    // OK example (always 2000)
    sharedCounter = 0;
    std::thread t3(safeIncrement);
    std::thread t4(safeIncrement);
    t3.join();
    t4.join();
    std::cout << "OK: " << sharedCounter << " (expected: 2000)" << std::endl;
    return 0;
}

Overview

std::thread is the thread class standardized in C++11. Passing a function (or callable object) and its arguments to the constructor immediately starts the thread. The started thread must either be waited on with join() or detached with detach(). If neither is called before the thread object's destructor runs, std::terminate is called and the program aborts. Always use std::mutex for mutual exclusion when accessing shared data between threads. Using std::lock_guard releases the lock automatically via RAII when the scope ends, making the code exception-safe. At compile time, link the thread library with -lpthread (Linux) or -pthread. The number of logical cores returned by std::thread::hardware_concurrency() provides a useful guideline for how many threads to create.

If you find any errors or copyright issues, please .