<C++> 多线程基础

1. 引言

在现代计算机系统中,多核处理器已成为标配。为了充分利用硬件资源,提高程序性能,多线程编程成为C++开发者必须掌握的核心技能之一。C++11标准引入了原生的多线程支持,使得编写跨平台的多线程程序变得更加简单和安全。

本文将系统性地介绍C++多线程编程的基础知识,涵盖线程创建与管理、互斥量与锁、条件变量、原子变量等核心概念。

2. 线程(Thread)

2.1 线程的基本概念

线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位。一个进程可以包含多个线程,这些线程共享进程的资源(如内存空间、文件描述符等),但拥有独立的执行栈和程序计数器。

2.2 C++中的线程创建

C++11通过<thread>头文件提供了线程支持。创建线程的基本方式如下:

cpp 复制代码
#include <iostream>
#include <thread>

void thread_function() {
    std::cout << "Hello from thread!" << std::endl;
}

int main() {
    // 创建线程并立即执行
    std::thread t(thread_function);
    
    // 等待线程执行完成
    t.join();
    
    std::cout << "Main thread finished" << std::endl;
    return 0;
}

2.3 线程的几种创建方式

cpp 复制代码
#include <iostream>
#include <thread>
#include <functional>

// 方式1:函数指针
void func1() {
    std::cout << "Function pointer thread" << std::endl;
}

// 方式2:Lambda表达式
auto lambda_func = []() {
    std::cout << "Lambda thread" << std::endl;
};

// 方式3:函数对象
class Functor {
public:
    void operator()() {
        std::cout << "Functor thread" << std::endl;
    }
};

// 方式4:成员函数
class MyClass {
public:
    void member_func() {
        std::cout << "Member function thread" << std::endl;
    }
};

int main() {
    // 1. 函数指针
    std::thread t1(func1);
    
    // 2. Lambda表达式
    std::thread t2(lambda_func);
    
    // 3. 函数对象
    std::thread t3(Functor());
    
    // 4. 成员函数
    MyClass obj;
    std::thread t4(&MyClass::member_func, &obj);
    
    t1.join();
    t2.join();
    t3.join();
    t4.join();
    
    return 0;
}

2.4 线程的分离与等待

cpp 复制代码
#include <iostream>
#include <thread>
#include <chrono>

void worker_thread() {
    std::this_thread::sleep_for(std::chrono::seconds(2));
    std::cout << "Worker thread finished" << std::endl;
}

int main() {
    std::thread t(worker_thread);
    
    // 分离线程(主线程不等待子线程)
    // t.detach();
    
    // 等待线程完成(推荐方式)
    t.join();
    
    // 检查线程是否可join
    if (t.joinable()) {
        t.join();
    }
    
    return 0;
}

3. 互斥量(Mutex)与锁(Lock)

3.1 数据竞争问题

当多个线程同时访问共享数据时,如果没有适当的同步机制,就会发生数据竞争(Data Race),导致未定义行为。

cpp 复制代码
#include <iostream>
#include <thread>
#include <vector>

int counter = 0;  // 共享数据

void increment() {
    for (int i = 0; i < 100000; ++i) {
        ++counter;  // 这里会发生数据竞争
    }
}

int main() {
    std::thread t1(increment);
    std::thread t2(increment);
    
    t1.join();
    t2.join();
    
    // 结果可能不是200000
    std::cout << "Counter: " << counter << std::endl;
    return 0;
}

3.2 互斥量的基本使用

cpp 复制代码
#include <iostream>
#include <thread>
#include <mutex>
#include <vector>

int counter = 0;
std::mutex mtx;  // 互斥量

void safe_increment() {
    for (int i = 0; i < 100000; ++i) {
        mtx.lock();    // 加锁
        ++counter;     // 临界区
        mtx.unlock();  // 解锁
    }
}

int main() {
    std::thread t1(safe_increment);
    std::thread t2(safe_increment);
    
    t1.join();
    t2.join();
    
    // 结果一定是200000
    std::cout << "Counter: " << counter << std::endl;
    return 0;
}

3.3 不同类型的互斥量

C++提供了多种互斥量类型,适用于不同的场景:

cpp 复制代码
#include <mutex>
#include <shared_mutex>
#include <thread>

// 1. std::mutex - 基本互斥量
std::mutex basic_mutex;

// 2. std::recursive_mutex - 可重入互斥量
std::recursive_mutex recursive_mutex;

// 3. std::timed_mutex - 带超时的互斥量
std::timed_mutex timed_mutex;

// 4. std::recursive_timed_mutex - 可重入带超时的互斥量
std::recursive_timed_mutex recursive_timed_mutex;

// 5. std::shared_mutex - 读写锁(C++17)
std::shared_mutex shared_mutex;

3.4 锁管理类(RAII风格)

手动管理锁的获取和释放容易出错,C++提供了几种锁管理类:

cpp 复制代码
#include <iostream>
#include <thread>
#include <mutex>
#include <vector>

int counter = 0;
std::mutex mtx;

void increment_with_lock_guard() {
    for (int i = 0; i < 100000; ++i) {
        // std::lock_guard - 最简单的RAII锁
        std::lock_guard<std::mutex> lock(mtx);
        ++counter;
    }
}

void increment_with_unique_lock() {
    for (int i = 0; i < 100000; ++i) {
        // std::unique_lock - 更灵活的RAII锁
        std::unique_lock<std::mutex> lock(mtx);
        ++counter;
        // 可以手动解锁
        lock.unlock();
        // 做一些不需要锁的操作
        // 可以重新加锁
        lock.lock();
        // 继续操作共享数据
    }
}

void increment_with_scoped_lock() {
    for (int i = 0; i < 100000; ++i) {
        // std::scoped_lock - C++17引入,可以锁多个互斥量
        std::scoped_lock lock(mtx);
        ++counter;
    }
}

int main() {
    std::thread t1(increment_with_lock_guard);
    std::thread t2(increment_with_unique_lock);
    std::thread t3(increment_with_scoped_lock);
    
    t1.join();
    t2.join();
    t3.join();
    
    std::cout << "Counter: " << counter << std::endl;
    return 0;
}

3.5 死锁与避免

cpp 复制代码
#include <iostream>
#include <thread>
#include <mutex>

std::mutex mtx1, mtx2;

void thread1() {
    // 错误的顺序 - 可能导致死锁
    mtx1.lock();
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
    mtx2.lock();
    
    std::cout << "Thread 1 acquired both locks" << std::endl;
    
    mtx2.unlock();
    mtx1.unlock();
}

void thread2() {
    // 错误的顺序 - 可能导致死锁
    mtx2.lock();
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
    mtx1.lock();
    
    std::cout << "Thread 2 acquired both locks" << std::endl;
    
    mtx1.unlock();
    mtx2.unlock();
}

void safe_thread1() {
    // 使用std::lock同时锁定多个互斥量,避免死锁
    std::lock(mtx1, mtx2);
    std::lock_guard<std::mutex> lock1(mtx1, std::adopt_lock);
    std::lock_guard<std::mutex> lock2(mtx2, std::adopt_lock);
    
    std::cout << "Safe Thread 1 acquired both locks" << std::endl;
}

void safe_thread2() {
    // 使用std::scoped_lock(C++17)
    std::scoped_lock lock(mtx1, mtx2);
    
    std::cout << "Safe Thread 2 acquired both locks" << std::endl;
}

int main() {
    // 可能导致死锁的示例
    // std::thread t1(thread1);
    // std::thread t2(thread2);
    
    // 安全的示例
    std::thread t1(safe_thread1);
    std::thread t2(safe_thread2);
    
    t1.join();
    t2.join();
    
    return 0;
}

4. 条件变量(Condition Variable)

4.1 条件变量的基本概念

条件变量用于线程间的同步,允许一个或多个线程等待某个条件成立。

cpp 复制代码
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <queue>

std::mutex mtx;
std::condition_variable cv;
std::queue<int> data_queue;
bool finished = false;

void producer() {
    for (int i = 0; i < 10; ++i) {
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
        
        {
            std::lock_guard<std::mutex> lock(mtx);
            data_queue.push(i);
            std::cout << "Produced: " << i << std::endl;
        }
        
        cv.notify_one();  // 通知一个等待的消费者
    }
    
    {
        std::lock_guard<std::mutex> lock(mtx);
        finished = true;
    }
    cv.notify_all();  // 通知所有等待的消费者
}

void consumer() {
    while (true) {
        std::unique_lock<std::mutex> lock(mtx);
        
        // 等待条件:队列不为空或生产完成
        cv.wait(lock, []() {
            return !data_queue.empty() || finished;
        });
        
        // 检查是否应该退出
        if (finished && data_queue.empty()) {
            break;
        }
        
        // 消费数据
        int data = data_queue.front();
        data_queue.pop();
        lock.unlock();
        
        std::cout << "Consumed: " << data << std::endl;
        
        // 模拟处理时间
        std::this_thread::sleep_for(std::chrono::milliseconds(50));
    }
}

int main() {
    std::thread prod(producer);
    std::thread cons1(consumer);
    std::thread cons2(consumer);
    
    prod.join();
    cons1.join();
    cons2.join();
    
    std::cout << "All threads finished" << std::endl;
    return 0;
}

4.2 条件变量的使用模式

cpp 复制代码
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>

class SimpleSemaphore {
private:
    std::mutex mtx;
    std::condition_variable cv;
    int count;
    
public:
    SimpleSemaphore(int initial = 0) : count(initial) {}
    
    void acquire() {
        std::unique_lock<std::mutex> lock(mtx);
        cv.wait(lock, [this]() { return count > 0; });
        --count;
    }
    
    void release() {
        std::unique_lock<std::mutex> lock(mtx);
        ++count;
        cv.notify_one();
    }
};

void worker(int id, SimpleSemaphore& sem) {
    sem.acquire();
    std::cout << "Worker " << id << " started" << std::endl;
    std::this_thread::sleep_for(std::chrono::seconds(1));
    std::cout << "Worker " << id << " finished" << std::endl;
    sem.release();
}

int main() {
    SimpleSemaphore sem(2);  // 允许同时运行2个worker
    
    std::thread workers[5];
    for (int i = 0; i < 5; ++i) {
        workers[i] = std::thread(worker, i, std::ref(sem));
    }
    
    for (auto& w : workers) {
        w.join();
    }
    
    return 0;
}

5. 原子变量(Atomic)

5.1 原子操作的基本概念

原子操作是不可分割的操作,要么完全执行,要么完全不执行,不会出现中间状态。

cpp 复制代码
#include <iostream>
#include <thread>
#include <atomic>
#include <vector>

// 使用原子变量
std::atomic<int> atomic_counter(0);

// 普通变量(用于对比)
int non_atomic_counter = 0;

void atomic_increment() {
    for (int i = 0; i < 100000; ++i) {
        ++atomic_counter;  // 原子操作
    }
}

void non_atomic_increment() {
    for (int i = 0; i < 100000; ++i) {
        ++non_atomic_counter;  // 非原子操作,可能发生数据竞争
    }
}

int main() {
    std::thread t1(atomic_increment);
    std::thread t2(atomic_increment);
    
    std::thread t3(non_atomic_increment);
    std::thread t4(non_atomic_increment);
    
    t1.join();
    t2.join();
    t3.join();
    t4.join();
    
    std::cout << "Atomic counter: " << atomic_counter << std::endl;
    std::cout << "Non-atomic counter: " << non_atomic_counter << std::endl;
    
    return 0;
}

5.2 原子变量的内存顺序

cpp 复制代码
#include <iostream>
#include <thread>
#include <atomic>

std::atomic<int> x(0), y(0);
int r1, r2;

void thread1() {
    x.store(1, std::memory_order_relaxed);
    r1 = y.load(std::memory_order_relaxed);
}

void thread2() {
    y.store(1, std::memory_order_relaxed);
    r2 = x.load(std::memory_order_relaxed);
}

void test_memory_order() {
    int count = 0;
    for (int i = 0; i < 10000; ++i) {
        x = 0;
        y = 0;
        r1 = 0;
        r2 = 0;
        
        std::thread t1(thread1);
        std::thread t2(thread2);
        
        t1.join();
        t2.join();
        
        if (r1 == 0 && r2 == 0) {
            ++count;
        }
    }
    
    std::cout << "Relaxed ordering: r1 == 0 && r2 == 0 happened " 
              << count << " times" << std::endl;
}

int main() {
    test_memory_order();
    return 0;
}

5.3 原子变量的高级操作

cpp 复制代码
#include <iostream>
#include <thread>
#include <atomic>
#include <vector>

std::atomic<int> counter(0);

void increment_with_fetch_add() {
    for (int i = 0; i < 100000; ++i) {
        // fetch_add返回旧值,然后加上给定值
        int old_value = counter.fetch_add(1);
        // 可以基于旧值做一些操作
    }
}

void compare_exchange_example() {
    int expected = 0;
    while (!counter.compare_exchange_weak(expected, expected + 1)) {
        // 如果counter != expected,expected会被更新为counter的当前值
        // 然后重试
    }
}

void atomic_operations() {
    std::atomic<int> value(10);
    
    // 各种原子操作
    value.store(20);                     // 存储
    int loaded = value.load();           // 加载
    
    value.fetch_add(5);                  // 原子加
    value.fetch_sub(3);                  // 原子减
    
    value.fetch_and(0xFF);               // 原子与
    value.fetch_or(0x0F);                // 原子或
    value.fetch_xor(0xAA);               // 原子异或
    
    // 交换
    int old = value.exchange(100);
    
    std::cout << "Final value: " << value.load() << std::endl;
}

int main() {
    std::thread t1(increment_with_fetch_add);
    std::thread t2(increment_with_fetch_add);
    
    t1.join();
    t2.join();
    
    std::cout << "Counter: " << counter << std::endl;
    
    atomic_operations();
    
    return 0;
}
相关推荐
·白小白1 小时前
C++ STL 容器 list 底层结构详解
开发语言·c++·list
BirdenT1 小时前
20260604紫题训练
c++·算法
tg:;1 小时前
Catkin 常用命令
开发语言·c++·算法
暖阳华笺1 小时前
【高频考点】回溯(暴力搜索)
数据结构·c++·算法·回溯法
hunterkkk(c++)1 小时前
学习dijkstra算法(c++)
c++·学习·算法
c++之路2 小时前
Linux 下 C++ 开发环境搭建
linux·运维·c++
thisiszdy2 小时前
<C++> 智能指针
开发语言·c++
玖玥拾2 小时前
C/C++ 基础笔记(五)
c语言·c++·指针
Flash.kkl3 小时前
C++基于websocketpp的多用户网页五子棋项目
开发语言·网络·数据库·c++·websocket·mysql