【Linux C/C++开发】第16章:多线程编程基础

第16章:多线程编程基础

学习目标

  • 🎯 理解进程与线程的核心概念及区别
  • 🎯 掌握线程的创建、管理和同步技术
  • 🎯 学会编写线程安全的代码
  • 🎯 理解并避免常见的并发问题(死锁、竞态条件)
  • 🎯 能够设计和实现简单的多线程应用程序

学习路线

第一阶段:基础概念(第1-2天)

1.1 进程与线程的本质区别

进程(Process):操作系统资源分配的基本单位

  • 拥有独立的地址空间
  • 拥有独立的文件描述符、信号处理等资源
  • 进程间通信需要特殊机制(管道、消息队列、共享内存等)
  • 创建和销毁开销较大

线程(Thread):CPU调度的基本单位

  • 共享同一进程的地址空间
  • 共享文件描述符、信号处理等资源
  • 线程间通信直接通过共享内存
  • 创建和销毁开销较小

生活中的类比

  • 进程就像不同的公司,每个公司有自己的办公楼、财务系统等
  • 线程就像公司内的员工,共享公司的资源,但各自执行不同的任务

核心思想:进程是"资源隔离"的概念,线程是"资源共享"的概念。就像不同的家庭住在不同的房子里(进程),而同一个家庭的成员共享同一所房子(线程)。

1.2 线程的生命周期

线程的三种基本状态

  • 新建(New):线程对象已创建,但尚未开始执行
  • 可运行(Runnable):线程已准备好运行,等待CPU调度
  • 终止(Terminated):线程执行完成或异常退出

就像员工的工作状态:新员工入职(新建)→等待分配任务(可运行)→完成任务离职(终止)

cpp 复制代码
#include <iostream>
#include <pthread.h>
#include <unistd.h>

void* thread_function(void* arg) {
    int thread_id = *(int*)arg;
    std::cout << "线程 " << thread_id << " 开始执行" << std::endl;
    
    // 模拟线程工作
    sleep(2);
    
    std::cout << "线程 " << thread_id << " 执行结束" << std::endl;
    return nullptr;
}

int main() {
    pthread_t thread;
    int thread_id = 1;
    
    std::cout << "主线程:创建子线程" << std::endl;
    
    // 创建线程(NEW -> RUNNABLE)
    int result = pthread_create(&thread, nullptr, thread_function, &thread_id);
    if (result != 0) {
        std::cerr << "线程创建失败" << std::endl;
        return 1;
    }
    
    std::cout << "主线程:等待子线程结束" << std::endl;
    
    // 等待线程结束(等待线程从RUNNABLE/RUNNING -> TERMINATED)
    pthread_join(thread, nullptr);
    
    std::cout << "主线程:子线程已结束" << std::endl;
    return 0;
}

性能优化分析

  • 第一次检查(无锁):避免每次访问都加锁,提高性能
  • 第二次检查(加锁):确保只有一个线程创建实例
  • pthread_once:更简洁的线程安全初始化机制,由操作系统保证只执行一次

设计精髓:双重检查锁定体现了"延迟初始化"和"性能优化"的平衡。既保证了线程安全,又避免了不必要的性能开销。

第二阶段:线程创建与管理(第3-4天)

2.1 线程创建详解

pthread_create函数就像招聘员工的流程

  • pthread_t *thread:给新员工分配工号
  • const pthread_attr_t *attr:设定员工的职位描述(nullptr表示默认职位)
  • void *(*start_routine)(void*):员工入职后要执行的具体工作任务
  • void *arg:交给员工的工具和资料
cpp 复制代码
#include <pthread.h>

int pthread_create(
    pthread_t *thread,           // 线程ID输出参数
    const pthread_attr_t *attr,  // 线程属性(nullptr表示默认)
    void *(*start_routine)(void*), // 线程入口函数
    void *arg                    // 传递给线程函数的参数
);

// 返回值:成功返回0,失败返回错误码

核心思想:线程创建的本质是"任务委托"。主线程把一部分工作委托给子线程,两者可以并行执行。就像项目经理把任务分配给团队成员,大家分工合作提高效率。

线程属性设置实战

cpp 复制代码
#include <pthread.h>
#include <iostream>

void* thread_function(void* arg) {
    std::cout << "线程运行中..." << std::endl;
    return nullptr;
}

int main() {
    pthread_t thread;
    pthread_attr_t attr;
    
    // 初始化线程属性
    pthread_attr_init(&attr);
    
    // 设置线程为分离状态(结束后自动释放资源)
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
    
    // 设置线程栈大小(例如2MB)
    size_t stack_size = 2 * 1024 * 1024;
    pthread_attr_setstacksize(&attr, stack_size);
    
    // 创建线程
    int result = pthread_create(&thread, &attr, thread_function, nullptr);
    if (result != 0) {
        std::cerr << "线程创建失败" << std::endl;
        return 1;
    }
    
    // 销毁线程属性对象
    pthread_attr_destroy(&attr);
    
    // 对于分离线程,不需要join
    sleep(1); // 给线程时间执行
    
    return 0;
}
2.2 线程参数传递的最佳实践

值传递 vs 指针传递

cpp 复制代码
#include <pthread.h>
#include <iostream>
#include <string>

// 结构体传递多个参数
struct ThreadData {
    int thread_id;
    std::string message;
    int counter;
};

void* thread_function(void* arg) {
    ThreadData* data = (ThreadData*)arg;
    
    std::cout << "线程ID: " << data->thread_id << std::endl;
    std::cout << "消息: " << data->message << std::endl;
    std::cout << "计数器: " << data->counter << std::endl;
    
    // 修改数据
    data->counter += 100;
    
    return nullptr;
}

int main() {
    pthread_t threads[3];
    ThreadData thread_data[3];
    
    // 创建多个线程
    for (int i = 0; i < 3; i++) {
        thread_data[i].thread_id = i;
        thread_data[i].message = "线程 " + std::to_string(i) + " 的消息";
        thread_data[i].counter = i * 10;
        
        int result = pthread_create(&threads[i], nullptr, thread_function, &thread_data[i]);
        if (result != 0) {
            std::cerr << "线程 " << i << " 创建失败" << std::endl;
        }
    }
    
    // 等待所有线程结束
    for (int i = 0; i < 3; i++) {
        pthread_join(threads[i], nullptr);
        std::cout << "线程 " << i << " 结束后的计数器: " << thread_data[i].counter << std::endl;
    }
    
    return 0;
}

第三阶段:线程同步技术(第5-7天)

3.1 互斥锁(Mutex)实战

互斥锁的本质:就像洗手间的门锁,一次只能有一个人进入。当多个人(线程)需要共用同一个资源时,互斥锁确保"互斥访问"------一次只有一个线程能操作共享资源。

银行账户并发操作示例

想象一个共享银行账户,多个人同时操作会发生什么?如果没有互斥锁,可能出现这种情况:

  • 张三看到余额1000元
  • 李四看到余额1000元
  • 张三存入100元(应该变成1100元)
  • 李四取出200元(基于他看到的1000元,应该剩800元)
  • 结果可能是800元而不是正确的900元!

这就是"竞态条件"------多个线程竞争操作同一数据,导致结果不确定。

cpp 复制代码
#include <pthread.h>
#include <iostream>
#include <unistd.h>
#include <random>

class BankAccount {
private:
    double balance;
    pthread_mutex_t mutex;
    
public:
    BankAccount(double initial_balance) : balance(initial_balance) {
        pthread_mutex_init(&mutex, nullptr);
    }
    
    ~BankAccount() {
        pthread_mutex_destroy(&mutex);
    }
    
    // 存款
    void deposit(double amount) {
        pthread_mutex_lock(&mutex);
        
        // 模拟处理时间
        usleep(1000);
        
        double old_balance = balance;
        balance += amount;
        
        std::cout << "存款 " << amount << " 元,余额从 " << old_balance 
                  << " 变为 " << balance << std::endl;
        
        pthread_mutex_unlock(&mutex);
    }
    
    // 取款
    bool withdraw(double amount) {
        pthread_mutex_lock(&mutex);
        
        // 模拟处理时间
        usleep(1000);
        
        if (balance >= amount) {
            double old_balance = balance;
            balance -= amount;
            
            std::cout << "取款 " << amount << " 元,余额从 " << old_balance 
                      << " 变为 " << balance << std::endl;
            
            pthread_mutex_unlock(&mutex);
            return true;
        } else {
            std::cout << "取款失败,余额不足。当前余额: " << balance << std::endl;
            pthread_mutex_unlock(&mutex);
            return false;
        }
    }
    
    double get_balance() {
        pthread_mutex_lock(&mutex);
        double current_balance = balance;
        pthread_mutex_unlock(&mutex);
        return current_balance;
    }
};

void* customer_thread(void* arg) {
    BankAccount* account = (BankAccount*)arg;
    std::random_device rd;
    std::mt19937 gen(rd());
    std::uniform_int_distribution<> dis(1, 100);
    
    for (int i = 0; i < 5; i++) {
        int action = dis(gen) % 2;
        double amount = dis(gen);
        
        if (action == 0) {
            account->deposit(amount);
        } else {
            account->withdraw(amount);
        }
        
        usleep(100000); // 100ms
    }
    
    return nullptr;
}

int main() {
    BankAccount account(1000.0); // 初始余额1000元
    pthread_t customers[5];
    
    std::cout << "银行系统启动,初始余额: " << account.get_balance() << " 元" << std::endl;
    
    // 创建5个客户线程
    for (int i = 0; i < 5; i++) {
        pthread_create(&customers[i], nullptr, customer_thread, &account);
    }
    
    // 等待所有客户线程结束
    for (int i = 0; i < 5; i++) {
        pthread_join(customers[i], nullptr);
    }
    
    std::cout << "所有交易完成,最终余额: " << account.get_balance() << " 元" << std::endl;
    
    return 0;
}
3.2 条件变量(Condition Variable)实战

条件变量的本质:就像餐厅的"叫号系统"。当缓冲区满时,生产者(厨师)等待;当缓冲区空时,消费者(顾客)等待。条件变量提供了一种"等待某个条件满足"的机制。

生产者-消费者问题经典实现

想象一个餐厅厨房:

  • 生产者 = 厨师:制作食物放入缓冲区(餐台)
  • 消费者 = 服务员:从缓冲区取出食物给顾客
  • 缓冲区 = 餐台:存放待送出的食物
  • 条件变量 = 叫号系统:协调厨师和服务员的工作

当餐台满时,厨师等待;当餐台空时,服务员等待。条件变量就是协调这种"等待-通知"关系的机制。

cpp 复制代码
#include <pthread.h>
#include <iostream>
#include <queue>
#include <unistd.h>
#include <random>

const int BUFFER_SIZE = 10;
const int NUM_PRODUCERS = 3;
const int NUM_CONSUMERS = 2;
const int NUM_ITEMS = 20;

template<typename T>
class BoundedBuffer {
private:
    std::queue<T> buffer;
    int capacity;
    
    pthread_mutex_t mutex;
    pthread_cond_t can_produce;
    pthread_cond_t can_consume;
    
public:
    BoundedBuffer(int cap) : capacity(cap) {
        pthread_mutex_init(&mutex, nullptr);
        pthread_cond_init(&can_produce, nullptr);
        pthread_cond_init(&can_consume, nullptr);
    }
    
    ~BoundedBuffer() {
        pthread_mutex_destroy(&mutex);
        pthread_cond_destroy(&can_produce);
        pthread_cond_destroy(&can_consume);
    }
    
    void produce(const T& item) {
        pthread_mutex_lock(&mutex);
        
        // 等待缓冲区有空位
        while (buffer.size() >= capacity) {
            std::cout << "缓冲区满,生产者等待..." << std::endl;
            pthread_cond_wait(&can_produce, &mutex);
        }
        
        // 生产物品
        buffer.push(item);
        std::cout << "生产物品: " << item << ",缓冲区大小: " << buffer.size() << std::endl;
        
        // 通知消费者
        pthread_cond_signal(&can_consume);
        pthread_mutex_unlock(&mutex);
    }
    
    T consume() {
        pthread_mutex_lock(&mutex);
        
        // 等待缓冲区有物品
        while (buffer.empty()) {
            std::cout << "缓冲区空,消费者等待..." << std::endl;
            pthread_cond_wait(&can_consume, &mutex);
        }
        
        // 消费物品
        T item = buffer.front();
        buffer.pop();
        std::cout << "消费物品: " << item << ",缓冲区大小: " << buffer.size() << std::endl;
        
        // 通知生产者
        pthread_cond_signal(&can_produce);
        pthread_mutex_unlock(&mutex);
        
        return item;
    }
};

struct ProducerData {
    int id;
    BoundedBuffer<int>* buffer;
    int items_to_produce;
};

struct ConsumerData {
    int id;
    BoundedBuffer<int>* buffer;
    int items_to_consume;
};

void* producer(void* arg) {
    ProducerData* data = (ProducerData*)arg;
    std::random_device rd;
    std::mt19937 gen(rd());
    std::uniform_int_distribution<> dis(1, 100);
    
    for (int i = 0; i < data->items_to_produce; i++) {
        int item = dis(gen);
        data->buffer->produce(item);
        usleep(100000); // 100ms
    }
    
    std::cout << "生产者 " << data->id << " 完成生产" << std::endl;
    return nullptr;
}

void* consumer(void* arg) {
    ConsumerData* data = (ConsumerData*)arg;
    
    for (int i = 0; i < data->items_to_consume; i++) {
        int item = data->buffer->consume();
        usleep(200000); // 200ms
    }
    
    std::cout << "消费者 " << data->id << " 完成消费" << std::endl;
    return nullptr;
}

int main() {
    BoundedBuffer<int> buffer(BUFFER_SIZE);
    pthread_t producers[NUM_PRODUCERS];
    pthread_t consumers[NUM_CONSUMERS];
    
    ProducerData producer_data[NUM_PRODUCERS];
    ConsumerData consumer_data[NUM_CONSUMERS];
    
    std::cout << "=== 生产者-消费者问题演示 ===" << std::endl;
    
    // 创建生产者线程
    for (int i = 0; i < NUM_PRODUCERS; i++) {
        producer_data[i].id = i;
        producer_data[i].buffer = &buffer;
        producer_data[i].items_to_produce = NUM_ITEMS / NUM_PRODUCERS;
        pthread_create(&producers[i], nullptr, producer, &producer_data[i]);
    }
    
    // 创建消费者线程
    for (int i = 0; i < NUM_CONSUMERS; i++) {
        consumer_data[i].id = i;
        consumer_data[i].buffer = &buffer;
        consumer_data[i].items_to_consume = NUM_ITEMS / NUM_CONSUMERS;
        pthread_create(&consumers[i], nullptr, consumer, &consumer_data[i]);
    }
    
    // 等待所有线程结束
    for (int i = 0; i < NUM_PRODUCERS; i++) {
        pthread_join(producers[i], nullptr);
    }
    
    for (int i = 0; i < NUM_CONSUMERS; i++) {
        pthread_join(consumers[i], nullptr);
    }
    
    std::cout << "=== 所有生产消费活动完成 ===" << std::endl;
    
    return 0;
}
3.3 读写锁(Read-Write Lock)实战

读写锁的核心思想:就像图书馆的借阅规则。多个读者可以同时读同一本书(共享读),但写者(编辑)修改时必须独占访问。读写锁区分"读操作"和"写操作",提高了并发性能。

适用场景:读多写少的情况。比如配置文件、缓存数据、系统设置等。

缓存系统实现

读写锁就像新闻编辑部的协作机制:

  • 记者(读者):可以同时阅读新闻稿,获取信息
  • 编辑(写者):修改新闻稿时必须独占,防止其他人同时修改
  • 主编(读写锁):协调记者和编辑的工作,确保数据一致性
cpp 复制代码
#include <pthread.h>
#include <iostream>
#include <unordered_map>
#include <string>
#include <unistd.h>
#include <random>

template<typename Key, typename Value>
class ThreadSafeCache {
private:
    std::unordered_map<Key, Value> cache;
    pthread_rwlock_t rwlock;
    
public:
    ThreadSafeCache() {
        pthread_rwlock_init(&rwlock, nullptr);
    }
    
    ~ThreadSafeCache() {
        pthread_rwlock_destroy(&rwlock);
    }
    
    // 获取值(读操作)
    bool get(const Key& key, Value& value) {
        pthread_rwlock_rdlock(&rwlock);
        
        auto it = cache.find(key);
        if (it != cache.end()) {
            value = it->second;
            pthread_rwlock_unlock(&rwlock);
            return true;
        }
        
        pthread_rwlock_unlock(&rwlock);
        return false;
    }
    
    // 设置值(写操作)
    void set(const Key& key, const Value& value) {
        pthread_rwlock_wrlock(&rwlock);
        
        cache[key] = value;
        
        pthread_rwlock_unlock(&rwlock);
    }
    
    // 删除键(写操作)
    bool remove(const Key& key) {
        pthread_rwlock_wrlock(&rwlock);
        
        bool existed = cache.erase(key) > 0;
        
        pthread_rwlock_unlock(&rwlock);
        return existed;
    }
    
    // 获取缓存大小(读操作)
    size_t size() {
        pthread_rwlock_rdlock(&rwlock);
        
        size_t cache_size = cache.size();
        
        pthread_rwlock_unlock(&rwlock);
        return cache_size;
    }
    
    // 清空缓存(写操作)
    void clear() {
        pthread_rwlock_wrlock(&rwlock);
        
        cache.clear();
        
        pthread_rwlock_unlock(&rwlock);
    }
};

void* reader_thread(void* arg) {
    ThreadSafeCache<int, std::string>* cache = (ThreadSafeCache<int, std::string>*)arg;
    std::random_device rd;
    std::mt19937 gen(rd());
    std::uniform_int_distribution<> dis(1, 100);
    
    for (int i = 0; i < 10; i++) {
        int key = dis(gen) % 20;
        std::string value;
        
        if (cache->get(key, value)) {
            std::cout << "读取缓存: key=" << key << ", value=" << value << std::endl;
        } else {
            std::cout << "缓存未命中: key=" << key << std::endl;
        }
        
        usleep(50000); // 50ms
    }
    
    return nullptr;
}

void* writer_thread(void* arg) {
    ThreadSafeCache<int, std::string>* cache = (ThreadSafeCache<int, std::string>*)arg;
    std::random_device rd;
    std::mt19937 gen(rd());
    std::uniform_int_distribution<> dis(1, 100);
    
    for (int i = 0; i < 5; i++) {
        int key = dis(gen) % 20;
        std::string value = "value_" + std::to_string(dis(gen));
        
        cache->set(key, value);
        std::cout << "写入缓存: key=" << key << ", value=" << value << std::endl;
        
        usleep(100000); // 100ms
    }
    
    return nullptr;
}

int main() {
    ThreadSafeCache<int, std::string> cache;
    pthread_t readers[3];
    pthread_t writers[2];
    
    std::cout << "=== 缓存系统读写锁演示 ===" << std::endl;
    
    // 预填充一些数据
    for (int i = 0; i < 10; i++) {
        cache.set(i, "initial_value_" + std::to_string(i));
    }
    
    std::cout << "初始缓存大小: " << cache.size() << std::endl;
    
    // 创建读者线程
    for (int i = 0; i < 3; i++) {
        pthread_create(&readers[i], nullptr, reader_thread, &cache);
    }
    
    // 创建写者线程
    for (int i = 0; i < 2; i++) {
        pthread_create(&writers[i], nullptr, writer_thread, &cache);
    }
    
    // 等待所有线程结束
    for (int i = 0; i < 3; i++) {
        pthread_join(readers[i], nullptr);
    }
    
    for (int i = 0; i < 2; i++) {
        pthread_join(writers[i], nullptr);
    }
    
    std::cout << "最终缓存大小: " << cache.size() << std::endl;
    
    return 0;
}

第四阶段:高级同步机制(第8-9天)

4.1 信号量(Semaphore)实战

信号量的本质:就像停车场的剩余车位显示器。信号量维护一个计数器,表示可用资源的数量。当线程需要资源时,计数器减1;当线程释放资源时,计数器加1。当计数器为0时,请求资源的线程必须等待。

与互斥锁的区别

  • 互斥锁:就像洗手间的钥匙,只有一把,一次只能一个人用
  • 信号量:就像停车场的多个车位,可以同时有多辆车停放

资源池管理示例

想象一个工具仓库的管理系统:

  • 信号量 = 剩余工具数量显示器
  • 工人 = 需要工具的线程
  • 工具 = 有限的共享资源
  • 仓库管理员 = 信号量管理机制
cpp 复制代码
#include <pthread.h>
#include <iostream>
#include <semaphore.h>
#include <vector>
#include <unistd.h>
#include <random>

class ResourcePool {
private:
    std::vector<int> resources;
    sem_t available_resources;
    pthread_mutex_t mutex;
    
public:
    ResourcePool(int pool_size) : resources(pool_size) {
        // 初始化信号量,初始值为资源池大小
        sem_init(&available_resources, 0, pool_size);
        pthread_mutex_init(&mutex, nullptr);
        
        // 初始化资源
        for (int i = 0; i < pool_size; i++) {
            resources[i] = i + 1;
        }
    }
    
    ~ResourcePool() {
        sem_destroy(&available_resources);
        pthread_mutex_destroy(&mutex);
    }
    
    // 获取资源
    int acquire_resource() {
        // 等待可用资源(信号量减1)
        sem_wait(&available_resources);
        
        pthread_mutex_lock(&mutex);
        
        // 找到并分配一个资源
        int resource_id = -1;
        for (size_t i = 0; i < resources.size(); i++) {
            if (resources[i] != 0) {
                resource_id = resources[i];
                resources[i] = 0; // 标记为已分配
                break;
            }
        }
        
        pthread_mutex_unlock(&mutex);
        
        return resource_id;
    }
    
    // 释放资源
    void release_resource(int resource_id) {
        pthread_mutex_lock(&mutex);
        
        // 归还资源到资源池
        for (size_t i = 0; i < resources.size(); i++) {
            if (resources[i] == 0) {
                resources[i] = resource_id;
                break;
            }
        }
        
        pthread_mutex_unlock(&mutex);
        
        // 增加可用资源计数(信号量加1)
        sem_post(&available_resources);
    }
    
    // 获取当前可用资源数量
    int get_available_count() {
        int count;
        sem_getvalue(&available_resources, &count);
        return count;
    }
};

struct WorkerData {
    int id;
    ResourcePool* pool;
    int tasks_to_complete;
};

void* worker_thread(void* arg) {
    WorkerData* data = (WorkerData*)arg;
    std::random_device rd;
    std::mt19937 gen(rd());
    std::uniform_int_distribution<> task_time_dis(100000, 500000); // 100-500ms
    
    for (int i = 0; i < data->tasks_to_complete; i++) {
        std::cout << "工人 " << data->id << " 请求资源..." << std::endl;
        
        // 获取资源
        int resource_id = data->pool->acquire_resource();
        
        if (resource_id != -1) {
            std::cout << "工人 " << data->id << " 获得资源 " << resource_id 
                      << ",可用资源: " << data->pool->get_available_count() << std::endl;
            
            // 使用资源完成任务
            usleep(task_time_dis(gen));
            
            std::cout << "工人 " << data->id << " 完成任务,释放资源 " << resource_id << std::endl;
            
            // 释放资源
            data->pool->release_resource(resource_id);
        }
        
        // 短暂休息
        usleep(100000); // 100ms
    }
    
    std::cout << "工人 " << data->id << " 完成所有任务" << std::endl;
    return nullptr;
}

int main() {
    const int POOL_SIZE = 3;
    const int NUM_WORKERS = 5;
    const int TASKS_PER_WORKER = 4;
    
    ResourcePool pool(POOL_SIZE);
    pthread_t workers[NUM_WORKERS];
    WorkerData worker_data[NUM_WORKERS];
    
    std::cout << "=== 资源池管理系统演示 ===" << std::endl;
    std::cout << "资源池大小: " << POOL_SIZE << std::endl;
    std::cout << "工人数量: " << NUM_WORKERS << std::endl;
    std::cout << "每个工人任务数: " << TASKS_PER_WORKER << std::endl;
    std::cout << "初始可用资源: " << pool.get_available_count() << std::endl;
    
    // 创建工作线程
    for (int i = 0; i < NUM_WORKERS; i++) {
        worker_data[i].id = i;
        worker_data[i].pool = &pool;
        worker_data[i].tasks_to_complete = TASKS_PER_WORKER;
        pthread_create(&workers[i], nullptr, worker_thread, &worker_data[i]);
    }
    
    // 等待所有工人完成
    for (int i = 0; i < NUM_WORKERS; i++) {
        pthread_join(workers[i], nullptr);
    }
    
    std::cout << "所有工人完成任务,最终可用资源: " << pool.get_available_count() << std::endl;
    
    return 0;
}
4.2 屏障(Barrier)实战

屏障的本质:就像团队建设中的"集合点"。所有团队成员必须到达集合点后,才能一起进行下一个活动。屏障确保所有线程都到达某个同步点后,才能继续执行后续操作。

适用场景:分阶段并行计算、MapReduce算法、并行排序等多步骤算法。

并行计算同步示例

屏障就像接力赛的交接区:

  • 第一棒 = 数据预处理阶段
  • 第二棒 = 主要计算阶段
  • 交接区 = 屏障同步点
  • 所有队员 = 并行工作的线程

只有所有线程都完成第一阶段后,才能一起进入第二阶段。

cpp 复制代码
#include <pthread.h>
#include <iostream>
#include <vector>
#include <unistd.h>
#include <cmath>

class ParallelCalculator {
private:
    std::vector<double> data;
    std::vector<double> results;
    pthread_barrier_t barrier;
    int num_threads;
    
public:
    ParallelCalculator(const std::vector<double>& input_data, int threads) 
        : data(input_data), results(input_data.size()), num_threads(threads) {
        pthread_barrier_init(&barrier, nullptr, num_threads);
    }
    
    ~ParallelCalculator() {
        pthread_barrier_destroy(&barrier);
    }
    
    struct ThreadData {
        int thread_id;
        int start_idx;
        int end_idx;
        ParallelCalculator* calculator;
    };
    
    // 阶段1:数据预处理
    static void* preprocessing_stage(void* arg) {
        ThreadData* data = (ThreadData*)arg;
        ParallelCalculator* calc = data->calculator;
        
        std::cout << "线程 " << data->thread_id << " 开始预处理阶段" << std::endl;
        
        // 模拟数据预处理
        for (int i = data->start_idx; i < data->end_idx; i++) {
            calc->results[i] = calc->data[i] * 2.0; // 简单的预处理
            usleep(100000); // 模拟处理时间
        }
        
        std::cout << "线程 " << data->thread_id << " 预处理完成,等待其他线程..." << std::endl;
        pthread_barrier_wait(&calc->barrier);
        
        return nullptr;
    }
    
    // 阶段2:主要计算
    static void* computation_stage(void* arg) {
        ThreadData* data = (ThreadData*)arg;
        ParallelCalculator* calc = data->calculator;
        
        // 等待所有线程完成预处理
        pthread_barrier_wait(&calc->barrier);
        
        std::cout << "线程 " << data->thread_id << " 开始计算阶段" << std::endl;
        
        // 模拟复杂计算
        for (int i = data->start_idx; i < data->end_idx; i++) {
            calc->results[i] = std::sin(calc->results[i]) + std::cos(calc->results[i]);
            usleep(200000); // 模拟更长的计算时间
        }
        
        std::cout << "线程 " << data->thread_id << " 计算完成" << std::endl;
        
        return nullptr;
    }
    
    void execute_parallel_computation() {
        pthread_t threads[num_threads];
        ThreadData thread_data[num_threads];
        
        int chunk_size = data.size() / num_threads;
        
        std::cout << "=== 并行计算演示 ===" << std::endl;
        std::cout << "数据大小: " << data.size() << std::endl;
        std::cout << "线程数量: " << num_threads << std::endl;
        std::cout << "每个线程处理: " << chunk_size << " 个数据" << std::endl;
        
        // 创建线程执行预处理
        for (int i = 0; i < num_threads; i++) {
            thread_data[i].thread_id = i;
            thread_data[i].start_idx = i * chunk_size;
            thread_data[i].end_idx = (i == num_threads - 1) ? data.size() : (i + 1) * chunk_size;
            thread_data[i].calculator = this;
            
            pthread_create(&threads[i], nullptr, preprocessing_stage, &thread_data[i]);
        }
        
        // 等待预处理完成
        for (int i = 0; i < num_threads; i++) {
            pthread_join(threads[i], nullptr);
        }
        
        // 重置屏障用于计算阶段
        pthread_barrier_destroy(&barrier);
        pthread_barrier_init(&barrier, nullptr, num_threads);
        
        // 创建线程执行计算
        for (int i = 0; i < num_threads; i++) {
            pthread_create(&threads[i], nullptr, computation_stage, &thread_data[i]);
        }
        
        // 等待计算完成
        for (int i = 0; i < num_threads; i++) {
            pthread_join(threads[i], nullptr);
        }
        
        std::cout << "并行计算完成!" << std::endl;
    }
    
    void print_results() {
        std::cout << "计算结果: ";
        for (size_t i = 0; i < results.size() && i < 10; i++) {
            std::cout << results[i] << " ";
        }
        std::cout << (results.size() > 10 ? "..." : "") << std::endl;
    }
};

int main() {
    // 创建测试数据
    std::vector<double> test_data(100);
    for (size_t i = 0; i < test_data.size(); i++) {
        test_data[i] = i * 0.1;
    }
    
    ParallelCalculator calculator(test_data, 4);
    calculator.execute_parallel_computation();
    calculator.print_results();
    
    return 0;
}

第五阶段:线程安全与死锁预防(第10-11天)

5.1 死锁预防策略

死锁的本质:就像交通堵塞在十字路口。四个方向的车都占据了路口的一部分,互相等待对方让路,结果谁也走不了。

死锁的四个必要条件

  1. 互斥条件:资源一次只能被一个线程占用
  2. 占有且等待:线程已占有资源,同时等待其他资源
  3. 不可抢占:资源不能被强制剥夺
  4. 循环等待:线程之间形成环形等待关系

死锁预防的核心思想:破坏死锁的四个必要条件之一。

银行家算法简化版

就像银行的贷款审批系统:

  • 银行家 = 操作系统
  • 资金 = 系统资源
  • 客户 = 线程/进程
  • 贷款申请 = 资源请求

银行家必须确保:即使所有客户同时申请最大额度的贷款,银行也能满足至少一个客户的需求,避免大家都拿不到钱的僵局。

cpp 复制代码
#include <pthread.h>
#include <iostream>
#include <vector>
#include <algorithm>
#include <unistd.h>
#include <random>

class DeadlockPrevention {
private:
    std::vector<pthread_mutex_t> resources;
    std::vector<int> resource_allocation;
    std::vector<int> resource_max;
    int num_resources;
    pthread_mutex_t safety_mutex;
    
public:
    DeadlockPrevention(int num_res) : num_resources(num_res) {
        resources.resize(num_res);
        resource_allocation.resize(num_res, 0);
        resource_max.resize(num_res, 1); // 每个资源最大数量为1
        
        for (int i = 0; i < num_res; i++) {
            pthread_mutex_init(&resources[i], nullptr);
        }
        pthread_mutex_init(&safety_mutex, nullptr);
    }
    
    ~DeadlockPrevention() {
        for (int i = 0; i < num_resources; i++) {
            pthread_mutex_destroy(&resources[i]);
        }
        pthread_mutex_destroy(&safety_mutex);
    }
    
    // 按顺序获取多个资源(死锁预防策略)
    bool acquire_resources_ordered(const std::vector<int>& resource_ids) {
        // 复制并排序资源ID,确保所有线程按相同顺序获取资源
        std::vector<int> sorted_ids = resource_ids;
        std::sort(sorted_ids.begin(), sorted_ids.end());
        
        pthread_mutex_lock(&safety_mutex);
        
        // 检查资源是否可用
        for (int res_id : sorted_ids) {
            if (resource_allocation[res_id] >= resource_max[res_id]) {
                pthread_mutex_unlock(&safety_mutex);
                return false; // 资源不可用
            }
        }
        
        // 按顺序获取所有资源
        for (int res_id : sorted_ids) {
            pthread_mutex_lock(&resources[res_id]);
            resource_allocation[res_id]++;
            std::cout << "获取资源 " << res_id << std::endl;
        }
        
        pthread_mutex_unlock(&safety_mutex);
        return true;
    }
    
    // 释放资源
    void release_resources(const std::vector<int>& resource_ids) {
        pthread_mutex_lock(&safety_mutex);
        
        for (int res_id : resource_ids) {
            if (resource_allocation[res_id] > 0) {
                resource_allocation[res_id]--;
                pthread_mutex_unlock(&resources[res_id]);
                std::cout << "释放资源 " << res_id << std::endl;
            }
        }
        
        pthread_mutex_unlock(&safety_mutex);
    }
    
    // 获取当前资源分配状态
    void print_status() {
        pthread_mutex_lock(&safety_mutex);
        
        std::cout << "资源分配状态: ";
        for (int i = 0; i < num_resources; i++) {
            std::cout << "R" << i << ":" << resource_allocation[i] << "/" << resource_max[i] << " ";
        }
        std::cout << std::endl;
        
        pthread_mutex_unlock(&safety_mutex);
    }
};

struct ProcessData {
    int process_id;
    DeadlockPrevention* dp;
    std::vector<int> needed_resources;
    int work_time;
};

void* process_thread(void* arg) {
    ProcessData* data = (ProcessData*)arg;
    std::random_device rd;
    std::mt19937 gen(rd());
    std::uniform_int_distribution<> time_dis(100000, 500000); // 100-500ms
    
    std::cout << "进程 " << data->process_id << " 开始,需要资源: ";
    for (int res : data->needed_resources) {
        std::cout << res << " ";
    }
    std::cout << std::endl;
    
    // 尝试获取资源
    if (data->dp->acquire_resources_ordered(data->needed_resources)) {
        std::cout << "进程 " << data->process_id << " 获得所有资源,开始工作" << std::endl;
        
        // 模拟工作
        usleep(time_dis(gen));
        
        std::cout << "进程 " << data->process_id << " 工作完成,准备释放资源" << std::endl;
        
        // 释放资源
        data->dp->release_resources(data->needed_resources);
        
        std::cout << "进程 " << data->process_id << " 释放资源完成" << std::endl;
    } else {
        std::cout << "进程 " << data->process_id << " 无法获取资源,将被阻塞" << std::endl;
    }
    
    return nullptr;
}

int main() {
    const int NUM_RESOURCES = 5;
    const int NUM_PROCESSES = 4;
    
    DeadlockPrevention dp(NUM_RESOURCES);
    pthread_t processes[NUM_PROCESSES];
    ProcessData process_data[NUM_PROCESSES];
    
    std::cout << "=== 死锁预防演示 ===" << std::endl;
    std::cout << "资源数量: " << NUM_RESOURCES << std::endl;
    std::cout << "进程数量: " << NUM_PROCESSES << std::endl;
    
    dp.print_status();
    
    // 定义每个进程需要的资源(注意:故意设计可能产生死锁的资源请求)
    std::vector<std::vector<int>> process_needs = {
        {0, 1, 2},    // 进程0需要资源0,1,2
        {2, 3, 4},    // 进程1需要资源2,3,4
        {1, 3},       // 进程2需要资源1,3
        {0, 4}        // 进程3需要资源0,4
    };
    
    // 创建进程线程
    for (int i = 0; i < NUM_PROCESSES; i++) {
        process_data[i].process_id = i;
        process_data[i].dp = &dp;
        process_data[i].needed_resources = process_needs[i];
        pthread_create(&processes[i], nullptr, process_thread, &process_data[i]);
        
        // 稍微延迟创建,增加并发复杂性
        usleep(100000);
    }
    
    // 等待所有进程结束
    for (int i = 0; i < NUM_PROCESSES; i++) {
        pthread_join(processes[i], nullptr);
    }
    
    std::cout << "所有进程执行完成" << std::endl;
    dp.print_status();
    
    return 0;
}
5.2 线程安全单例模式

单例模式的本质:确保一个类在整个程序运行期间只有一个实例。就像公司的总经理职位,无论多少人要联系总经理,最终都指向同一个人。

线程安全的挑战:多线程环境下,可能多个线程同时创建实例,导致"重复创建"问题。

双重检查锁定模式

就像公司招聘总经理的流程:

  1. 第一次检查:看总经理是否已经上任(无锁,快速检查)
  2. 加锁:如果发现总经理职位空缺,启动正式招聘流程
  3. 第二次检查:在招聘委员会内部再次确认,防止多个委员会同时招聘
  4. 正式任命:确认无误后,正式任命总经理

这种"双重检查"机制既保证了线程安全,又避免了每次访问都加锁的性能开销。

cpp 复制代码
#include <pthread.h>
#include <iostream>
#include <memory>
#include <unistd.h>

class ThreadSafeSingleton {
private:
    static ThreadSafeSingleton* instance;
    static pthread_mutex_t mutex;
    static pthread_once_t once_control;
    
    int data;
    
    // 私有构造函数
    ThreadSafeSingleton() : data(0) {
        std::cout << "单例对象创建" << std::endl;
    }
    
    // 初始化函数(只执行一次)
    static void init_instance() {
        instance = new ThreadSafeSingleton();
    }
    
public:
    // 删除拷贝构造函数和赋值运算符
    ThreadSafeSingleton(const ThreadSafeSingleton&) = delete;
    ThreadSafeSingleton& operator=(const ThreadSafeSingleton&) = delete;
    
    // 线程安全的获取实例方法(使用pthread_once)
    static ThreadSafeSingleton* get_instance() {
        pthread_once(&once_control, init_instance);
        return instance;
    }
    
    // 另一种实现:双重检查锁定
    static ThreadSafeSingleton* get_instance_dcl() {
        if (instance == nullptr) { // 第一次检查(无锁)
            pthread_mutex_lock(&mutex);
            if (instance == nullptr) { // 第二次检查(加锁)
                instance = new ThreadSafeSingleton();
            }
            pthread_mutex_unlock(&mutex);
        }
        return instance;
    }
    
    void set_data(int value) {
        pthread_mutex_lock(&mutex);
        data = value;
        pthread_mutex_unlock(&mutex);
    }
    
    int get_data() {
        pthread_mutex_lock(&mutex);
        int value = data;
        pthread_mutex_unlock(&mutex);
        return value;
    }
    
    void print_info() {
        pthread_mutex_lock(&mutex);
        std::cout << "单例对象地址: " << this << ", 数据: " << data << std::endl;
        pthread_mutex_unlock(&mutex);
    }
};

// 静态成员定义
ThreadSafeSingleton* ThreadSafeSingleton::instance = nullptr;
pthread_mutex_t ThreadSafeSingleton::mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_once_t ThreadSafeSingleton::once_control = PTHREAD_ONCE_INIT;

void* singleton_test_thread(void* arg) {
    int thread_id = *(int*)arg;
    
    // 获取单例实例
    ThreadSafeSingleton* singleton = ThreadSafeSingleton::get_instance();
    
    // 设置数据
    singleton->set_data(thread_id * 100);
    
    // 模拟一些工作
    usleep(100000);
    
    // 读取数据
    int data = singleton->get_data();
    
    std::cout << "线程 " << thread_id << " 读取数据: " << data << std::endl;
    
    // 打印单例信息
    singleton->print_info();
    
    return nullptr;
}

int main() {
    const int NUM_THREADS = 10;
    pthread_t threads[NUM_THREADS];
    int thread_ids[NUM_THREADS];
    
    std::cout << "=== 线程安全单例模式演示 ===" << std::endl;
    
    // 创建多个线程同时访问单例
    for (int i = 0; i < NUM_THREADS; i++) {
        thread_ids[i] = i;
        pthread_create(&threads[i], nullptr, singleton_test_thread, &thread_ids[i]);
    }
    
    // 等待所有线程结束
    for (int i = 0; i < NUM_THREADS; i++) {
        pthread_join(threads[i], nullptr);
    }
    
    std::cout << "所有线程执行完成" << std::endl;
    
    // 验证单例的唯一性
    ThreadSafeSingleton* instance1 = ThreadSafeSingleton::get_instance();
    ThreadSafeSingleton* instance2 = ThreadSafeSingleton::get_instance();
    
    std::cout << "实例1地址: " << instance1 << std::endl;
    std::cout << "实例2地址: " << instance2 << std::endl;
    std::cout << "是否为同一实例: " << (instance1 == instance2 ? "是" : "否") << std::endl;
    
    return 0;
}

第六阶段:综合实践项目(第12-14天)

6.1 多线程Web服务器模拟

简单HTTP服务器实现

cpp 复制代码
#include <pthread.h>
#include <iostream>
#include <string>
#include <vector>
#include <queue>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <cstring>
#include <sstream>
#include <random>

// 简单的HTTP请求结构
struct HttpRequest {
    std::string method;
    std::string path;
    std::string version;
    std::string body;
    
    HttpRequest() = default;
    HttpRequest(const std::string& req_str) {
        parse_request(req_str);
    }
    
    void parse_request(const std::string& req_str) {
        std::istringstream iss(req_str);
        iss >> method >> path >> version;
        
        // 读取剩余的请求体
        std::string line;
        while (std::getline(iss, line)) {
            body += line + "\n";
        }
    }
};

// 线程池任务
struct Task {
    int client_socket;
    std::string client_ip;
    int client_port;
};

class ThreadPool {
private:
    std::queue<Task> task_queue;
    pthread_mutex_t queue_mutex;
    pthread_cond_t queue_not_empty;
    pthread_cond_t queue_not_full;
    std::vector<pthread_t> workers;
    bool shutdown;
    int max_queue_size;
    
public:
    ThreadPool(int num_threads, int max_tasks) : shutdown(false), max_queue_size(max_tasks) {
        pthread_mutex_init(&queue_mutex, nullptr);
        pthread_cond_init(&queue_not_empty, nullptr);
        pthread_cond_init(&queue_not_full, nullptr);
        
        workers.resize(num_threads);
        for (int i = 0; i < num_threads; i++) {
            pthread_create(&workers[i], nullptr, worker_thread, this);
        }
    }
    
    ~ThreadPool() {
        shutdown = true;
        pthread_cond_broadcast(&queue_not_empty);
        
        for (int i = 0; i < workers.size(); i++) {
            pthread_join(workers[i], nullptr);
        }
        
        pthread_mutex_destroy(&queue_mutex);
        pthread_cond_destroy(&queue_not_empty);
        pthread_cond_destroy(&queue_not_full);
    }
    
    void submit_task(const Task& task) {
        pthread_mutex_lock(&queue_mutex);
        
        while (task_queue.size() >= max_queue_size && !shutdown) {
            pthread_cond_wait(&queue_not_full, &queue_mutex);
        }
        
        if (!shutdown) {
            task_queue.push(task);
            pthread_cond_signal(&queue_not_empty);
        }
        
        pthread_mutex_unlock(&queue_mutex);
    }
    
private:
    static void* worker_thread(void* arg) {
        ThreadPool* pool = (ThreadPool*)arg;
        
        while (true) {
            pthread_mutex_lock(&pool->queue_mutex);
            
            while (pool->task_queue.empty() && !pool->shutdown) {
                pthread_cond_wait(&pool->queue_not_empty, &pool->queue_mutex);
            }
            
            if (pool->shutdown) {
                pthread_mutex_unlock(&pool->queue_mutex);
                break;
            }
            
            Task task = pool->task_queue.front();
            pool->task_queue.pop();
            pthread_cond_signal(&pool->queue_not_full);
            
            pthread_mutex_unlock(&pool->queue_mutex);
            
            // 处理任务
            handle_http_request(task);
        }
        
        return nullptr;
    }
    
    static void handle_http_request(const Task& task) {
        std::cout << "线程 " << pthread_self() << " 处理请求从 " 
                  << task.client_ip << ":" << task.client_port << std::endl;
        
        // 读取HTTP请求
        char buffer[1024];
        int bytes_read = recv(task.client_socket, buffer, sizeof(buffer) - 1, 0);
        
        if (bytes_read > 0) {
            buffer[bytes_read] = '\0';
            HttpRequest request(buffer);
            
            std::cout << "收到请求: " << request.method << " " << request.path << std::endl;
            
            // 模拟处理时间
            usleep(100000); // 100ms
            
            // 生成HTTP响应
            std::string response = generate_response(request);
            
            // 发送响应
            send(task.client_socket, response.c_str(), response.length(), 0);
        }
        
        // 关闭客户端连接
        close(task.client_socket);
        
        std::cout << "线程 " << pthread_self() << " 完成请求处理" << std::endl;
    }
    
    static std::string generate_response(const HttpRequest& request) {
        std::ostringstream response;
        
        if (request.path == "/") {
            response << "HTTP/1.1 200 OK\r\n";
            response << "Content-Type: text/html\r\n";
            response << "\r\n";
            response << "<html><body><h1>多线程Web服务器</h1><p>请求路径: " << request.path << "</p></body></html>";
        } else if (request.path == "/status") {
            response << "HTTP/1.1 200 OK\r\n";
            response << "Content-Type: text/plain\r\n";
            response << "\r\n";
            response << "服务器状态: 运行中\n";
            response << "线程池大小: 4\n";
            response << "队列最大长度: 10\n";
        } else {
            response << "HTTP/1.1 404 Not Found\r\n";
            response << "Content-Type: text/plain\r\n";
            response << "\r\n";
            response << "404 页面未找到";
        }
        
        return response.str();
    }
};

class SimpleWebServer {
private:
    int server_socket;
    int port;
    ThreadPool* thread_pool;
    bool running;
    
public:
    SimpleWebServer(int p) : port(p), server_socket(-1), thread_pool(nullptr), running(false) {}
    
    ~SimpleWebServer() {
        stop();
    }
    
    bool start() {
        // 创建服务器socket
        server_socket = socket(AF_INET, SOCK_STREAM, 0);
        if (server_socket < 0) {
            std::cerr << "创建socket失败" << std::endl;
            return false;
        }
        
        // 设置socket选项
        int opt = 1;
        setsockopt(server_socket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
        
        // 绑定地址
        struct sockaddr_in server_addr;
        server_addr.sin_family = AF_INET;
        server_addr.sin_port = htons(port);
        server_addr.sin_addr.s_addr = INADDR_ANY;
        
        if (bind(server_socket, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
            std::cerr << "绑定地址失败" << std::endl;
            close(server_socket);
            return false;
        }
        
        // 开始监听
        if (listen(server_socket, 10) < 0) {
            std::cerr << "监听失败" << std::endl;
            close(server_socket);
            return false;
        }
        
        // 创建线程池
        thread_pool = new ThreadPool(4, 10);
        running = true;
        
        std::cout << "Web服务器启动,监听端口: " << port << std::endl;
        return true;
    }
    
    void run() {
        std::cout << "服务器运行中,等待客户端连接..." << std::endl;
        
        while (running) {
            struct sockaddr_in client_addr;
            socklen_t client_len = sizeof(client_addr);
            
            int client_socket = accept(server_socket, (struct sockaddr*)&client_addr, &client_len);
            
            if (client_socket < 0) {
                if (running) {
                    std::cerr << "接受连接失败" << std::endl;
                }
                continue;
            }
            
            // 创建任务
            Task task;
            task.client_socket = client_socket;
            task.client_ip = inet_ntoa(client_addr.sin_addr);
            task.client_port = ntohs(client_addr.sin_port);
            
            std::cout << "收到连接来自: " << task.client_ip << ":" << task.client_port << std::endl;
            
            // 提交任务到线程池
            thread_pool->submit_task(task);
        }
    }
    
    void stop() {
        running = false;
        
        if (server_socket >= 0) {
            close(server_socket);
            server_socket = -1;
        }
        
        if (thread_pool) {
            delete thread_pool;
            thread_pool = nullptr;
        }
        
        std::cout << "Web服务器已停止" << std::endl;
    }
};

int main() {
    SimpleWebServer server(8080);
    
    if (server.start()) {
        std::cout << "=== 多线程Web服务器演示 ===" << std::endl;
        std::cout << "服务器地址: http://localhost:8080" << std::endl;
        std::cout << "可用路径: /, /status" << std::endl;
        std::cout << "按 Ctrl+C 停止服务器" << std::endl;
        
        server.run();
    }
    
    return 0;
}

实践练习项目

项目1:多线程文件处理器

需求描述

创建一个多线程程序,能够并发处理多个文件:

  • 一个线程负责读取文件列表
  • 多个工作线程负责处理文件内容
  • 一个线程负责输出处理结果
  • 使用线程安全的数据结构进行通信

实现框架

cpp 复制代码
#include <pthread.h>
#include <iostream>
#include <queue>
#include <string>
#include <vector>
#include <fstream>
#include <sstream>
#include <unistd.h>

// 文件任务结构
struct FileTask {
    std::string filename;
    std::string content;
    std::string result;
    bool processed;
    
    FileTask() : processed(false) {}
    FileTask(const std::string& fname) : filename(fname), processed(false) {}
};

// 线程安全的任务队列
class FileTaskQueue {
private:
    std::queue<FileTask> tasks;
    pthread_mutex_t mutex;
    pthread_cond_t not_empty;
    pthread_cond_t not_full;
    bool finished;
    size_t max_size;
    
public:
    FileTaskQueue(size_t max_q_size) : finished(false), max_size(max_q_size) {
        pthread_mutex_init(&mutex, nullptr);
        pthread_cond_init(&not_empty, nullptr);
        pthread_cond_init(&not_full, nullptr);
    }
    
    ~FileTaskQueue() {
        pthread_mutex_destroy(&mutex);
        pthread_cond_destroy(&not_empty);
        pthread_cond_destroy(&not_full);
    }
    
    void push(const FileTask& task) {
        pthread_mutex_lock(&mutex);
        
        while (tasks.size() >= max_size && !finished) {
            pthread_cond_wait(&not_full, &mutex);
        }
        
        if (!finished) {
            tasks.push(task);
            pthread_cond_signal(&not_empty);
        }
        
        pthread_mutex_unlock(&mutex);
    }
    
    bool pop(FileTask& task) {
        pthread_mutex_lock(&mutex);
        
        while (tasks.empty() && !finished) {
            pthread_cond_wait(&not_empty, &mutex);
        }
        
        if (!tasks.empty()) {
            task = tasks.front();
            tasks.pop();
            pthread_cond_signal(&not_full);
            pthread_mutex_unlock(&mutex);
            return true;
        }
        
        pthread_mutex_unlock(&mutex);
        return false;
    }
    
    void set_finished() {
        pthread_mutex_lock(&mutex);
        finished = true;
        pthread_cond_broadcast(&not_empty);
        pthread_cond_broadcast(&not_full);
        pthread_mutex_unlock(&mutex);
    }
    
    bool is_finished() {
        pthread_mutex_lock(&mutex);
        bool result = finished && tasks.empty();
        pthread_mutex_unlock(&mutex);
        return result;
    }
};

// 生产者线程:读取文件列表
void* file_reader_thread(void* arg) {
    FileTaskQueue* input_queue = (FileTaskQueue*)arg;
    std::vector<std::string> filenames = {
        "test1.txt", "test2.txt", "test3.txt", "test4.txt", "test5.txt"
    };
    
    // 创建测试文件
    for (const std::string& filename : filenames) {
        std::ofstream file(filename);
        file << "这是文件 " << filename << " 的内容\n";
        file << "包含多行文本数据\n";
        file << "用于多线程处理测试\n";
        file.close();
    }
    
    std::cout << "文件读取线程:开始读取文件列表" << std::endl;
    
    for (const std::string& filename : filenames) {
        FileTask task(filename);
        
        // 读取文件内容
        std::ifstream file(filename);
        if (file.is_open()) {
            std::stringstream buffer;
            buffer << file.rdbuf();
            task.content = buffer.str();
            file.close();
            
            std::cout << "读取文件: " << filename << " (" << task.content.length() << " 字符)" << std::endl;
            
            input_queue->push(task);
        } else {
            std::cerr << "无法读取文件: " << filename << std::endl;
        }
    }
    
    input_queue->set_finished();
    std::cout << "文件读取线程:完成" << std::endl;
    
    return nullptr;
}

// 工作线程:处理文件内容
void* file_processor_thread(void* arg) {
    struct ThreadData {
        FileTaskQueue* input_queue;
        FileTaskQueue* output_queue;
        int thread_id;
    };
    
    ThreadData* data = (ThreadData*)arg;
    
    std::cout << "处理器线程 " << data->thread_id << ": 开始工作" << std::endl;
    
    FileTask task;
    while (data->input_queue->pop(task)) {
        std::cout << "处理器线程 " << data->thread_id << ": 处理文件 " << task.filename << std::endl;
        
        // 模拟文件处理(例如:统计字符数、转换为大写等)
        std::string processed_content = "[已处理] " + task.content;
        
        // 转换为大写
        for (char& c : processed_content) {
            if (c >= 'a' && c <= 'z') {
                c = c - 'a' + 'A';
            }
        }
        
        task.result = processed_content;
        task.processed = true;
        
        // 模拟处理时间
        usleep(200000); // 200ms
        
        data->output_queue->push(task);
        
        std::cout << "处理器线程 " << data->thread_id << ": 完成处理 " << task.filename << std::endl;
    }
    
    data->output_queue->set_finished();
    std::cout << "处理器线程 " << data->thread_id << ": 结束工作" << std::endl;
    
    return nullptr;
}

// 消费者线程:输出处理结果
void* result_writer_thread(void* arg) {
    FileTaskQueue* output_queue = (FileTaskQueue*)arg;
    
    std::cout << "结果写入线程:开始输出结果" << std::endl;
    
    FileTask task;
    while (output_queue->pop(task)) {
        if (task.processed) {
            std::cout << "结果写入线程:文件 " << task.filename << " 处理结果:" << std::endl;
            std::cout << "原始内容长度: " << task.content.length() << " 字符" << std::endl;
            std::cout << "处理后内容长度: " << task.result.length() << " 字符" << std::endl;
            std::cout << "预览: " << task.result.substr(0, 50) << "..." << std::endl;
            std::cout << "---" << std::endl;
        }
    }
    
    std::cout << "结果写入线程:完成" << std::endl;
    
    return nullptr;
}

int main() {
    std::cout << "=== 多线程文件处理器 ===" << std::endl;
    
    // 创建任务队列
    FileTaskQueue input_queue(10);
    FileTaskQueue output_queue(10);
    
    pthread_t reader_thread;
    pthread_t processor_threads[3];
    pthread_t writer_thread;
    
    // 启动生产者线程
    pthread_create(&reader_thread, nullptr, file_reader_thread, &input_queue);
    
    // 启动工作线程
    struct ThreadData {
        FileTaskQueue* input_queue;
        FileTaskQueue* output_queue;
        int thread_id;
    } processor_data[3];
    
    for (int i = 0; i < 3; i++) {
        processor_data[i].input_queue = &input_queue;
        processor_data[i].output_queue = &output_queue;
        processor_data[i].thread_id = i;
        pthread_create(&processor_threads[i], nullptr, file_processor_thread, &processor_data[i]);
    }
    
    // 启动消费者线程
    pthread_create(&writer_thread, nullptr, result_writer_thread, &output_queue);
    
    // 等待所有线程完成
    pthread_join(reader_thread, nullptr);
    
    for (int i = 0; i < 3; i++) {
        pthread_join(processor_threads[i], nullptr);
    }
    
    pthread_join(writer_thread, nullptr);
    
    std::cout << "所有处理完成!" << std::endl;
    
    // 清理测试文件
    std::vector<std::string> filenames = {
        "test1.txt", "test2.txt", "test3.txt", "test4.txt", "test5.txt"
    };
    
    for (const std::string& filename : filenames) {
        std::remove(filename.c_str());
    }
    
    return 0;
}

学习总结与进阶路径

本周掌握的核心技能

  1. 线程基础:理解进程与线程的区别,掌握线程创建、终止、连接等基本操作
  2. 同步机制:熟练使用互斥锁、条件变量、读写锁、信号量、屏障等同步原语
  3. 线程安全:能够编写线程安全的代码,避免竞态条件和死锁
  4. 并发设计:掌握生产者-消费者、读者-写者等经典并发模式

核心思想总结

并发编程的本质:协调多个执行流对共享资源的访问,在保证正确性的前提下提高性能。

五大同步机制对比

  • 互斥锁:独占访问,适合简单共享资源保护
  • 条件变量:等待-通知机制,适合生产者-消费者模式
  • 读写锁:共享读独占写,适合读多写少场景
  • 信号量:资源计数,适合管理有限资源池
  • 屏障:阶段同步,适合分阶段并行算法

设计哲学

  • 分离关注:区分"互斥"、"同步"、"通信"等不同需求
  • 最小化锁:减少锁的粒度和持有时间
  • 事件驱动:用条件变量替代忙等待
  • 有序竞争:通过规则预防死锁,而非事后处理

常见错误与调试技巧

死锁调试

cpp 复制代码
// 使用超时机制检测死锁
struct timespec timeout;
timeout.tv_sec = time(nullptr) + 5; // 5秒超时
timeout.tv_nsec = 0;

int result = pthread_mutex_timedlock(&mutex, &timeout);
if (result == ETIMEDOUT) {
    std::cerr << "可能的死锁检测到!" << std::endl;
}

线程泄漏检测

bash 复制代码
# 查看线程数量
ps -eLf | grep your_program_name

# 查看线程状态
cat /proc/[pid]/status | grep Threads

性能优化建议

  1. 减少锁粒度:尽量使用细粒度锁,减少锁竞争
  2. 避免虚假唤醒:条件变量等待时使用循环检查
  3. 合理使用线程数量:通常CPU核心数的1-2倍
  4. 考虑无锁编程:对于简单操作,使用原子操作替代锁

下一周学习预告

第17周将学习现代C++的并发编程特性:

  • C++11/14/17标准线程库
  • 原子操作和内存模型
  • 异步编程(std::async, std::future)
  • 并行算法库

这些现代特性将让你编写更安全、更高效的并发代码!

相关推荐
朕要睡了1 小时前
aws-sdk-cpp编译
linux·运维·服务器
nono牛1 小时前
Android Binder C/C++ 层详解与实践
android·c语言·binder
AA陈超1 小时前
以 Lyra 的架构为基础,创建一个名为 “Aura“ 的英雄并实现发射火球技能
c++·笔记·学习·ue5·lyra
刘国华-平价IT运维课堂1 小时前
红帽企业Linux 10.1发布:AI命令行助手、量子安全加密和混合云创新
linux·运维·服务器·人工智能·云计算
qq_479875432 小时前
bash Buffering
linux
间彧2 小时前
tail 、journalctl 、 docker logs -f命令详解
linux
西西学代码3 小时前
Flutter---Listview横向滚动列表(2)
linux·运维·flutter
xlq223223 小时前
16.17.list(上)
c++·list
cpp_25013 小时前
P1765 手机
数据结构·c++·算法·题解·洛谷