본문으로 건너뛰기
Previous
Next
C++ Multithreading Basics: std::thread, mutex,

C++ Multithreading Basics: std::thread, mutex,

C++ Multithreading Basics: std::thread, mutex,

이 글의 핵심

Introduction to C++ concurrency: std::thread, join/detach, mutex and lock_guard, producer–consumer patterns, thread pools, races, and deadlocks.

Create default thread

#include <iostream>
#include <thread>
using namespace std;
void printHello() {
    cout << "Hello from thread!" << endl;
}
int main() {
thread t(printHello);// create thread
t.join();// Wait for thread to terminate
    
    cout << "Main thread" << endl;
    
    return 0;
}

Lambda and parameters

#include <iostream>
#include <thread>
using namespace std;
int main() {
// use lambda
    thread t1( {
        cout << "Lambda thread" << endl;
    });
    
// Passing parameters
    thread t2( {
        cout << x << ": " << s << endl;
    }, 10, "Hello");
    
    t1.join();
    t2.join();
    
    return 0;
}

Synchronize with mutex

#include <iostream>
#include <thread>
#include <mutex>
using namespace std;
mutex mtx;
int counter = 0;
void increment() {
    for (int i = 0; i < 1000; i++) {
        mtx.lock();
        counter++;
        mtx.unlock();
    }
}
int main() {
    thread t1(increment);
    thread t2(increment);
    
    t1.join();
    t2.join();
    
    cout << "Counter: " << counter << endl;  // 2000
    
    return 0;
}

lock_guard (RAII)

#include <iostream>
#include <thread>
#include <mutex>
using namespace std;
mutex mtx;
void safeIncrement(int& counter) {
    for (int i = 0; i < 1000; i++) {
lock_guard<mutex> lock(mtx);  // unlocks automatically
        counter++;
} // automatically unlocked
}
int main() {
    int counter = 0;
    
    thread t1(safeIncrement, ref(counter));
    thread t2(safeIncrement, ref(counter));
    
    t1.join();
    t2.join();
    
    cout << "Counter: " << counter << endl;  // 2000
    
    return 0;
}

Practical example

Example 1: Parallel computation

#include <iostream>
#include <thread>
#include <vector>
using namespace std;
void sumRange(int start, int end, long long& result) {
    long long sum = 0;
    for (int i = start; i < end; i++) {
        sum += i;
    }
    result = sum;
}
int main() {
    const int N = 100000000;
    const int NUM_THREADS = 4;
    
    vector<thread> threads;
    vector<long long> results(NUM_THREADS);
    
    int range = N / NUM_THREADS;
    
// create thread
    for (int i = 0; i < NUM_THREADS; i++) {
        int start = i * range;
        int end = (i == NUM_THREADS - 1) ? N : (i + 1) * range;
        threads.emplace_back(sumRange, start, end, ref(results[i]));
    }
    
// wait for all threads
    for (auto& t : threads) {
        t.join();
    }
    
// sum up results
    long long total = 0;
    for (long long r : results) {
        total += r;
    }
    
    cout << "sum: " << total << endl;
    
    return 0;
}

Description: Split large calculations into multiple threads for parallel processing.

Example 2: Producer-Consumer Pattern

#include <iostream>
#include <thread>
#include <queue>
#include <mutex>
#include <condition_variable>
using namespace std;
queue<int> dataQueue;
mutex mtx;
condition_variable cv;
bool done = false;
void producer() {
    for (int i = 1; i <= 10; i++) {
        this_thread::sleep_for(chrono::milliseconds(100));
        
        {
            lock_guard<mutex> lock(mtx);
            dataQueue.push(i);
            cout << "produce: " << i << endl;
        }
        
cv.notify_one();// Notify consumer
    }
    
    {
        lock_guard<mutex> lock(mtx);
        done = true;
    }
    cv.notify_all();
}
void consumer() {
    while (true) {
        unique_lock<mutex> lock(mtx);
        
        cv.wait(lock,  {
            return !dataQueue.empty() || done;
        });
        
        while (!dataQueue.empty()) {
            int value = dataQueue.front();
            dataQueue.pop();
            lock.unlock();
            
            cout << "consume: " << value << endl;
            this_thread::sleep_for(chrono::milliseconds(150));
            
            lock.lock();
        }
        
        if (done && dataQueue.empty()) {
            break;
        }
    }
}
int main() {
    thread prod(producer);
    thread cons(consumer);
    
    prod.join();
    cons.join();
    
    return 0;
}

Description: This is a pattern where a producer and consumer exchange data through a queue.

Example 3: Thread Pool

#include <iostream>
#include <thread>
#include <vector>
#include <queue>
#include <functional>
#include <mutex>
#include <condition_variable>
using namespace std;
class ThreadPool {
private:
    vector<thread> workers;
    queue<function<void()>> tasks;
    mutex mtx;
    condition_variable cv;
    bool stop;
    
public:
    ThreadPool(size_t numThreads) : stop(false) {
        for (size_t i = 0; i < numThreads; i++) {
            workers.emplace_back([this]() {
                while (true) {
                    function<void()> task;
                    
                    {
                        unique_lock<mutex> lock(mtx);
                        cv.wait(lock, [this]() {
                            return stop || !tasks.empty();
                        });
                        
                        if (stop && tasks.empty()) {
                            return;
                        }
                        
                        task = move(tasks.front());
                        tasks.pop();
                    }
                    
                    task();
                }
            });
        }
    }
    
    ~ThreadPool() {
        {
            unique_lock<mutex> lock(mtx);
            stop = true;
        }
        
        cv.notify_all();
        
        for (auto& worker : workers) {
            worker.join();
        }
    }
    
    void enqueue(function<void()> task) {
        {
            unique_lock<mutex> lock(mtx);
            tasks.push(task);
        }
        cv.notify_one();
    }
};
int main() {
    ThreadPool pool(4);
    
    for (int i = 1; i <= 10; i++) {
        pool.enqueue([i]() {
            cout << "task " << i << " start (thread " 
                 << this_thread::get_id() << ")" << endl;
            this_thread::sleep_for(chrono::seconds(1));
            cout << "task " << i << " done" << endl;
        });
    }
    
    this_thread::sleep_for(chrono::seconds(5));
    
    return 0;
}

Description: Efficiently distributes work to thread pools.

Frequently occurring problems

Problem 1: Race Condition

Symptoms: Results vary each time Cause: Accessing shared resources without synchronization dissolvent:

// ❌ Race conditions
int counter = 0;
void increment() {
    for (int i = 0; i < 1000; i++) {
counter++;// danger!
    }
}
// ✅ Protected by mutex
mutex mtx;
int counter = 0;
void increment() {
    for (int i = 0; i < 1000; i++) {
        lock_guard<mutex> lock(mtx);
        counter++;
    }
}

Problem 2: Deadlock

Symptom: Program freezes Cause: Waiting on different mutexes dissolvent:

// ❌ Deadlock possible
mutex mtx1, mtx2;
void func1() {
    lock_guard<mutex> lock1(mtx1);
    lock_guard<mutex> lock2(mtx2);
}
void func2() {
lock_guard<mutex> lock2(mtx2);// Different order!
    lock_guard<mutex> lock1(mtx1);
}
// ✅ Always lock in the same order
void func1() {
    lock_guard<mutex> lock1(mtx1);
    lock_guard<mutex> lock2(mtx2);
}
void func2() {
lock_guard<mutex> lock1(mtx1);// same order
    lock_guard<mutex> lock2(mtx2);
}
// ✅ Use scoped_lock (C++17)
void func() {
scoped_lock lock(mtx1, mtx2);// Automatically prevent deadlock
}

Problem 3: See dangling after detach

Symptom: Crashes or strange values Cause: The variable referenced by the thread is destroyed. dissolvent:

// ❌ Dangerous code
void badExample() {
    int data = 10;
    thread t([&data]() {
        this_thread::sleep_for(chrono::seconds(1));
cout << data << endl;// Already destroyed!
    });
    t.detach();
} // data destruction
// ✅ Capture by value
void goodExample() {
    int data = 10;
    thread t([data]() {
        this_thread::sleep_for(chrono::seconds(1));
cout << data << endl;// safety
    });
    t.detach();
}
// ✅ Wait with join
void betterExample() {
    int data = 10;
    thread t([&data]() {
        cout << data << endl;
    });
t.join();// atmosphere
}

FAQ

Q1: join vs detach?

A:

  • join: Wait for thread to terminate (recommended)
  • detach: Runs in background (needs caution)

Q2: How many threads should I create?

A: Typically, the number of CPU cores is sufficient.

unsigned int numThreads = thread::hardware_concurrency();

Q3: Are mutexes slow?

A: There is some overhead, but you should definitely use it if necessary.You might also consider atomic.

Q4: Which containers are thread-safe?

A: C++ standard containers are not thread-safe by default.Protect it with a mutex or use a concurrent library.

Q5: async vs thread?

A:

  • thread: low-level control
  • async: high level, simple (returns future)
auto future = async(launch::async,  {
    return 42;
});
cout << future.get() << endl;

Q6: When to use multithreading?

A:

  • Parallelize CPU-intensive tasks
  • Utilize I/O latency
  • Improved responsiveness (UI)

Good article to read together (internal link)

Here’s another article related to this topic.

Practical tips

These are tips that can be applied right away in practice.

Debugging tips

  • If you run into a problem, check the compiler warnings first.
  • Reproduce the problem with a simple test case

Performance Tips

  • Don’t optimize without profiling
  • Set measurable indicators first

Code review tips

  • Check in advance for areas that are frequently pointed out in code reviews.
  • Follow your team’s coding conventions

Practical checklist

This is what you need to check when applying this concept in practice.

Before writing code

  • Is this technique the best way to solve the current problem?
  • Can team members understand and maintain this code?
  • Does it meet the performance requirements?

Writing code

  • Have you resolved all compiler warnings?
  • Have you considered edge cases?
  • Is error handling appropriate?

When reviewing code

  • Is the intention of the code clear?
  • Are there enough test cases?
  • Is it documented? Use this checklist to reduce mistakes and improve code quality.

Keywords covered in this article (related search terms)

This article will be helpful if you search for C++, multithreading, thread, mutex, concurrency, etc.


같이 보면 좋은 글 (내부 링크)

이 주제와 연결되는 다른 글입니다.


이 글에서 다루는 키워드 (관련 검색어)

C++, multithreading, std::thread, mutex, concurrency, lock_guard 등으로 검색하시면 이 글이 도움이 됩니다.