C++线程、并发、并行

发布于:2025-02-11 ⋅ 阅读:(33) ⋅ 点赞:(0)

 【欢迎关注编码小哥,学习更多实用的编程方法和技巧】

一、线程、并发和并行的概念

1、线程定义:

  • 程序执行的最小单元
  • 轻量级的进程
  • 共享同一进程的内存空间

特征:

  • 独立的执行路径
  • 共享进程资源
  • 上下文切换开销较小

类型:

    用户线程

    内核线程

    协程

2、并发定义:

  • 多个任务在同一时间段内交替执行
  • 宏观同时进行,微观交替

特点:

  • 单核CPU也可实现
  • 通过时间片轮转
  • 提高系统响应性
  • 资源利用率提升

实现方式:

    进程间并发

    线程间并发

    协程并发

3、并行定义:

  • 多个任务同时执行
  • 物理上同时运行

特点:

  • 多核/多处理器
  • 真正同时执行
  • 性能显著提升

实现方式:

  • 多核处理
  • 分布式计算
  • GPU并行计算

4、并发 vs 并行

并发:

时间片交替执行
单核CPU
看似同时运行

A    B    C
--|--|--|

并行:

真正同时执行
多核CPU
实际同时运行

A B C
-----

5、并发编程模型

同步模型:

  • 阻塞
  • 非阻塞
  • 同步
  • 异步

通信模型:

    共享内存

    消息传递

    Actor模型

    CSP模型

6、并发编程挑战

常见问题:

  • 竞争条件
  • 死锁
  • 资源争用
  • 线程安全

解决策略:

    互斥锁

    信号量

    原子操作

    无锁编程

7、代码示例

并发示例:

// 并发执行
void concurrentTask() {
    std::thread t1([]{ 
        // 任务A 
    });
    
    std::thread t2([]{ 
        // 任务B 
    });

    t1.join();
    t2.join();
}

并行示例:

// 并行计算
void parallelCompute(std::vector<int>& data) {
    // 使用并行算法
    std::for_each(
        std::execution::par,  // 并行执行
        data.begin(), 
        data.end(), 
        [](int& value) {
            value *= 2;  // 并行处理每个元素
        }
    );
}

 8、并发级别

    进程级并发

    线程级并发

    指令级并行

    数据级并行

9、应用场景

适合并发/并行:

    科学计算

    图形渲染

    大数据处理

    网络服务器

    实时系统

10、性能考虑

性能影响因素:

  • 任务granularity
  • 通信开销
  • 同步机制
  • 硬件配置

优化策略:

  • 最小化锁竞争
  • 减少上下文切换
  • 负载均衡
  • 选择合适并发粒度
  • 现代C++并发支持

标准库支持:

  • <thread>
  • <mutex>
  • <condition_variable>
  • <future>
  • <atomic>

选择依据:

  • 任务特性
  • 硬件配置
  • 性能需求
  • 复杂度

建议:

  • 优先考虑并发
  • 评估并行收益
  • 使用高级抽象
  • 谨慎设计

 二、代码实践

1、函数指针创建线程

#include <iostream>
#include <thread>

// 普通函数
void threadFunction(int param) {
    std::cout << "Thread with param: " << param << std::endl;
}

int main() {
    // 使用函数指针创建线程
    std::thread t1(threadFunction, 42);
    t1.join();
    return 0;
}

2、多种函数指针类型

// 不同签名的函数指针
void simpleFunction() {
    std::cout << "Simple function" << std::endl;
}

void paramFunction(int x, double y) {
    std::cout << "Params: " << x << ", " << y << std::endl;
}

int main() {
    // 无参函数
    std::thread t1(simpleFunction);
    
    // 多参数函数
    std::thread t2(paramFunction, 10, 3.14);
    
    t1.join();
    t2.join();
}

3、函数指针与引用传递

void referenceFunction(int& value) {
    value *= 2;
}

int main() {
    int x = 5;
    
    // 使用std::ref传递引用
    std::thread t(referenceFunction, std::ref(x));
    t.join();
    
    std::cout << "Modified x: " << x << std::endl; // 输出10
}

4、类成员函数线程

class Worker {
public:
    void memberFunction(int param) {
        std::cout << "Member function: " << param << std::endl;
    }
    
    static void staticMemberFunction(int param) {
        std::cout << "Static member function: " << param << std::endl;
    }
};

int main() {
    Worker worker;
    
    // 成员函数线程
    std::thread t1(&Worker::memberFunction, &worker, 100);
    
    // 静态成员函数线程
    std::thread t2(Worker::staticMemberFunction, 200);
    
    t1.join();
    t2.join();
}

5、函数指针与Lambda结合

int main() {
    // 使用函数指针类型的Lambda
    auto lambdaFunc = [](int x) {
        std::cout << "Lambda thread: " << x << std::endl;
    };
    
    std::thread t(lambdaFunc, 42);
    t.join();
    
    // 动态函数指针
    typedef void (*FuncPtr)(int);
    FuncPtr ptr = lambdaFunc;
    
    std::thread t2(ptr, 100);
    t2.join();
}

 6、高级函数指针线程池

#include <vector>
#include <functional>
#include <mutex>

class ThreadPool {
private:
    std::vector<std::thread> threads;
    std::mutex mutex;

public:
    // 使用函数指针创建线程
    void addThread(void (*func)(int), int param) {
        std::lock_guard<std::mutex> lock(mutex);
        threads.emplace_back(func, param);
    }

    // 使用std::function更灵活
    void addThreadFunc(std::function<void()> func) {
        std::lock_guard<std::mutex> lock(mutex);
        threads.emplace_back(func);
    }

    void joinAll() {
        for (auto& t : threads) {
            if (t.joinable()) {
                t.join();
            }
        }
    }
};

// 示例函数
void workerFunc(int id) {
    std::cout << "Worker " << id << " running" << std::endl;
}

int main() {
    ThreadPool pool;
    
    // 使用函数指针创建线程
    pool.addThread(workerFunc, 1);
    pool.addThread(workerFunc, 2);
    
    // 使用Lambda
    pool.addThreadFunc([]() {
        std::cout << "Lambda thread" << std::endl;
    });
    
    pool.joinAll();
}

 7、安全的函数指针线程管理

class SafeThread {
private:
    std::unique_ptr<std::thread> thread;
    std::atomic<bool> stop{false};

public:
    template<typename Func, typename... Args>
    void start(Func&& func, Args&&... args) {
        // 确保之前的线程已结束
        if (thread && thread->joinable()) {
            thread->join();
        }
        
        // 创建新线程
        thread = std::make_unique<std::thread>(
            std::forward<Func>(func), 
            std::forward<Args>(args)...
        );
    }

    void stop() {
        stop = true;
        if (thread && thread->joinable()) {
            thread->join();
        }
    }

    ~SafeThread() {
        stop();
    }
};

 8、注意事项

  • 避免悬空指针
  • 使用std::ref传递引用
  • 注意生命周期管理
  • 使用智能指针管理资源
  • 考虑线程安全

9、性能建议

  • 尽量减少线程创建开销
  • 使用线程池
  • 避免频繁创建和销毁线程
  • 选择合适的线程数

10、错误处理

try {
    std::thread t(threadFunction);
    t.join();
} catch (const std::system_error& e) {
    std::cerr << "Thread error: " << e.what() << std::endl;
}


网站公告

今日签到

点亮在社区的每一天
去签到