c++ 多线程(一)

c++ 多线程

一、基本概念

全局函数可以作为std::thread的构造函数参数,创建新线程时会自动调用该函数。

  • 函数可以是普通函数、静态函数、lambda表达式
  • 线程启动时立即执行该函数
  • 函数执行完毕,线程自动结束

二、基本用法

示例1:最简单的全局函数

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

// 全局函数定义
void simpleTask() {
    std::cout << "线程ID: " << std::this_thread::get_id() 
              << " 正在执行简单任务" << std::endl;
    std::this_thread::sleep_for(std::chrono::seconds(1));
    std::cout << "任务完成" << std::endl;
}

int main() {
    std::cout << "主线程ID: " << std::this_thread::get_id() << std::endl;
    
    // 创建线程,传入全局函数指针
    std::thread t(simpleTask);
    
    // 等待线程完成
    t.join();
    
    return 0;
}

三、带参数的全局函数

示例2:传递参数给线程函数

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

// 带参数的全局函数
void printMessage(const std::string& message, int count) {
    for (int i = 0; i < count; ++i) {
        std::cout << "消息[" << i << "]: " << message 
                  << " (线程ID: " << std::this_thread::get_id() << ")" 
                  << std::endl;
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
    }
}

int main() {
    // 创建线程时传递参数
    std::thread t1(printMessage, "Hello, Thread!", 3);
    std::thread t2(printMessage, "另一个线程", 5);
    
    t1.join();
    t2.join();
    
    return 0;
}

四、不同类型的全局函数

示例3:静态函数

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

class MyClass {
public:
    static void staticTask(int value) {
        std::cout << "静态函数,值: " << value 
                  << " 线程ID: " << std::this_thread::get_id() 
                  << std::endl;
    }
};

// 普通全局函数
void globalTask(const char* name) {
    std::cout << "全局函数: " << name << std::endl;
}

int main() {
    // 使用静态函数作为线程入口
    std::thread t1(MyClass::staticTask, 42);
    
    // 使用普通全局函数
    std::thread t2(globalTask, "测试线程");
    
    // 使用函数指针
    void (*funcPtr)(const char*) = globalTask;
    std::thread t3(funcPtr, "函数指针");
    
    t1.join();
    t2.join();
    t3.join();
    
    return 0;
}

五、传递引用和指针参数

示例4:传递引用参数

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

// 修改共享数据的全局函数
void processVector(std::vector<int>& vec, int multiplier) {
    std::cout << "处理向量,大小: " << vec.size() 
              << " 线程ID: " << std::this_thread::get_id() 
              << std::endl;
    
    // 修改向量元素
    for (auto& num : vec) {
        num *= multiplier;
    }
}

// 传递指针
void processArray(int* arr, int size) {
    std::cout << "处理数组,大小: " << size << std::endl;
    for (int i = 0; i < size; ++i) {
        arr[i] += 10;
    }
}

int main() {
    // 示例1:传递引用(需要使用std::ref包装)
    std::vector<int> data = {1, 2, 3, 4, 5};
    
    std::thread t1(processVector, std::ref(data), 2);
    t1.join();
    
    std::cout << "处理后的向量: ";
    for (int num : data) {
        std::cout << num << " ";
    }
    std::cout << std::endl;
    
    // 示例2:传递指针
    int array[5] = {10, 20, 30, 40, 50};
    std::thread t2(processArray, array, 5);
    t2.join();
    
    std::cout << "处理后的数组: ";
    for (int i = 0; i < 5; ++i) {
        std::cout << array[i] << " ";
    }
    std::cout << std::endl;
    
    return 0;
}

六、返回值的处理

示例5:使用future获取返回值

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

// 计算向量和的全局函数
int calculateSum(const std::vector<int>& vec) {
    std::cout << "计算线程ID: " << std::this_thread::get_id() << std::endl;
    
    // 模拟耗时计算
    std::this_thread::sleep_for(std::chrono::seconds(1));
    
    int sum = std::accumulate(vec.begin(), vec.end(), 0);
    return sum;
}

// 返回多个值的函数
std::pair<int, double> calculateStats(const std::vector<int>& vec) {
    if (vec.empty()) return {0, 0.0};
    
    int sum = std::accumulate(vec.begin(), vec.end(), 0);
    double avg = static_cast<double>(sum) / vec.size();
    
    return {sum, avg};
}

int main() {
    std::vector<int> numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    
    // 方式1:使用std::async(内部使用线程)
    std::future<int> future1 = std::async(std::launch::async, calculateSum, numbers);
    
    // 方式2:使用packaged_task
    std::packaged_task<int(const std::vector<int>&)> task(calculateSum);
    std::future<int> future2 = task.get_future();
    
    std::thread t(std::move(task), numbers);
    
    // 获取结果
    std::cout << "Async结果: " << future1.get() << std::endl;
    std::cout << "Thread结果: " << future2.get() << std::endl;
    
    t.join();
    
    // 获取多个返回值
    std::packaged_task<std::pair<int, double>(const std::vector<int>&)> 
        statsTask(calculateStats);
    std::future<std::pair<int, double>> statsFuture = statsTask.get_future();
    
    std::thread statsThread(std::move(statsTask), numbers);
    
    auto [sum, avg] = statsFuture.get();
    std::cout << "\n统计结果:\n";
    std::cout << "总和: " << sum << "\n平均值: " << avg << std::endl;
    
    statsThread.join();
    
    return 0;
}

七、注意事项

关键注意事项

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

// 1. 线程参数是值传递(除非使用std::ref)
void demonstratePassing(int value, int& ref, const std::string& str) {
    value += 10;  // 修改的是副本
    ref += 10;    // 修改的是原始引用
    std::cout << "字符串: " << str << std::endl;
}

int main() {
    int a = 5;
    int b = 10;
    std::string msg = "测试";
    
    // 正确传递引用
    std::thread t(demonstratePassing, a, std::ref(b), msg);
    t.join();
    
    std::cout << "a = " << a << std::endl;  // 仍然是5
    std::cout << "b = " << b << std::endl;  // 变为20
    
    // 2. 确保参数生命周期
    {
        int* ptr = new int(42);
        std::thread t2([](int* p) {
            std::cout << "值: " << *p << std::endl;
            delete p;  // 在线程内清理
        }, ptr);
        t2.detach();
    }
    
    // 3. 异常安全处理
    try {
        std::thread t3([]() {
            throw std::runtime_error("线程内部异常");
        });
        
        // 确保线程在异常发生时也能被join
        t3.join();
    } catch (const std::exception& e) {
        std::cout << "捕获异常: " << e.what() << std::endl;
    }
    
    return 0;
}

选择建议

  • 简单任务 → 全局函数
  • 需要状态管理 → 函数对象或成员函数
  • 一次性使用 → lambda表达式
  • 需要返回值 → 使用std::future
相关推荐
匠心网络科技2 小时前
前端学习手册-JavaScript条件判断语句全解析(十八)
开发语言·前端·javascript·学习·ecmascript
神仙别闹2 小时前
基于C++生成树思想的迷宫生成算法
开发语言·c++·算法
海上彼尚2 小时前
Go之路 - 1.gomod指令
开发语言·后端·golang
我命由我123452 小时前
Java 开发使用 MyBatis PostgreSQL 问题:使用了特殊字符而没有正确转义
java·开发语言·数据库·postgresql·java-ee·mybatis·学习方法
C语言小火车2 小时前
红黑树(C/C++ 实现版)—— 用 “带配重的书架” 讲透本质
c语言·开发语言·c++·红黑树
阿里嘎多学长2 小时前
2025-12-10 GitHub 热点项目精选
开发语言·程序员·github·代码托管
snow123f2 小时前
Lambda 表达式怎么用
java·开发语言·线程
梓䈑2 小时前
【C++】C++11(右值引用和移动语义、可变参数模板 和 包装器)
java·开发语言·c++
好评1242 小时前
【C++】一篇吃透容器适配器三件套:从stack/queue/priority_queue到deque底层
c++·stl·queue·stack