c++ 多线程详解

C++11引入了多线程支持,使得在标准库中直接使用线程和同步机制成为可能。在多线程编程中,你可以使用多线程来并发地执行任务,以提高程序性能,尤其是在处理I/O密集型或计算密集型任务时。

多线程的基本概念

  • 线程(Thread):是程序执行的基本单位。在多线程编程中,一个程序可以包含多个并发执行的线程。
  • 主线程(Main Thread):通常是程序的入口点,也就是 main() 函数所在的线程。创建的其他线程称为子线程(Worker Thread)
  • 数据同步:多线程访问共享数据时,需要确保同步,以避免数据竞争(Race Condition)问题。

使用 C++ 多线程的基本步骤

  1. 创建线程
  2. 启动线程执行任务
  3. 同步线程或等待线程完成
  4. 处理线程中的共享数据

示例结构

C++ 多线程的主要类是 std::thread,它提供了线程的基本操作。C++11 提供了锁机制来管理线程同步,比如 std::mutexstd::lock_guard 等。

1. 创建和启动线程

最简单的方式是通过 std::thread 创建一个线程,并传递要执行的函数或方法。

示例1:创建一个简单的线程
#include <iostream>
#include <thread>

// 线程执行的函数
void myTask() {
    std::cout << "Thread is running!" << std::endl;
}

int main() {
    // 创建并启动一个线程
    std::thread t(myTask);

    // 等待线程执行完成
    t.join();

    std::cout << "Main thread finished." << std::endl;
    return 0;
}
解释:
  • std::thread t(myTask);:创建并启动一个线程,myTask 函数在新的线程中执行。
  • t.join();:主线程等待子线程执行完成。

2. 传递参数给线程

你可以传递参数给线程函数,C++11 提供了自动参数传递的机制。

示例2:传递参数给线程函数
#include <iostream>
#include <thread>

// 带参数的线程函数
void printNumbers(int start, int end) {
    for (int i = start; i <= end; ++i) {
        std::cout << "Number: " << i << std::endl;
    }
}

int main() {
    // 创建线程并传递参数
    std::thread t(printNumbers, 1, 5);

    // 等待线程执行完成
    t.join();

    return 0;
}
解释:
  • std::thread t(printNumbers, 1, 5);:线程函数 printNumbers 需要两个参数,1 和 5。

3. 使用 lambda 表达式创建线程

C++11 支持 lambda 表达式,你可以直接在创建线程时定义任务。

示例3:使用 lambda 创建线程
#include <iostream>
#include <thread>

int main() {
    // 使用 lambda 表达式创建线程
    std::thread t([] {
        for (int i = 0; i < 5; ++i) {
            std::cout << "Lambda thread: " << i << std::endl;
        }
    });

    // 等待线程执行完成
    t.join();

    return 0;
}

4. 等待线程完成 (join) 和分离线程 (detach)

  • join():主线程等待子线程完成任务。
  • detach():子线程与主线程分离,主线程不等待子线程结束,子线程将在后台运行并独立结束。
示例4:使用 detach() 分离线程
#include <iostream>
#include <thread>
#include <chrono>

void backgroundTask() {
    for (int i = 0; i < 5; ++i) {
        std::cout << "Background task running..." << std::endl;
        std::this_thread::sleep_for(std::chrono::seconds(1));  // 模拟耗时操作
    }
}

int main() {
    std::thread t(backgroundTask);

    // 分离线程,让它在后台独立运行
    t.detach();

    std::cout << "Main thread continues..." << std::endl;
    
    // 主线程继续工作,子线程在后台运行
    std::this_thread::sleep_for(std::chrono::seconds(6));  // 让主线程等待一会儿

    std::cout << "Main thread finished." << std::endl;

    return 0;
}
解释:
  • t.detach();:主线程和子线程分离,主线程不再等待子线程,子线程会在后台运行并自动结束。

5. 线程同步

当多个线程访问共享数据时,必须同步它们的操作,以避免数据竞争问题。C++11 提供了 std::mutex(互斥锁)来解决这个问题。

示例5:使用 std::mutex 同步线程
#include <iostream>
#include <thread>
#include <mutex>

std::mutex mtx;  // 互斥锁保护共享资源
int counter = 0;

void incrementCounter() {
    for (int i = 0; i < 1000; ++i) {
        std::lock_guard<std::mutex> lock(mtx);  // 加锁
        ++counter;  // 修改共享数据
    }
}

int main() {
    std::thread t1(incrementCounter);
    std::thread t2(incrementCounter);

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

    std::cout << "Final counter value: " << counter << std::endl;
    return 0;
}
解释:
  • std::mutex mtx;:创建互斥锁 mtx,防止多个线程同时修改 counter
  • std::lock_guard<std::mutex> lock(mtx);:自动加锁和解锁机制,确保每次只有一个线程能访问 counter

6. 使用 std::futurestd::async

std::async 可以用来启动异步任务,并返回一个 std::future 对象,供主线程查询任务的执行结果。

示例6:使用 std::async 启动异步任务
#include <iostream>
#include <future>

int task(int a, int b) {
    return a + b;
}

int main() {
    // 使用 std::async 启动异步任务,并获取 std::future 对象
    std::future<int> result = std::async(std::launch::async, task, 10, 20);

    // 获取异步任务的返回值
    int value = result.get();
    std::cout << "Result: " << value << std::endl;

    return 0;
}
解释:
  • std::async:启动异步任务,指定任务为 task(10, 20)
  • result.get():获取异步任务的执行结果。

7. 处理多线程的共享数据和竞争条件

竞争条件(Race Condition)发生在多个线程同时访问和修改共享资源时,结果依赖于线程的执行顺序。

避免竞争条件的关键:
  • 互斥锁(Mutex):用来保护共享资源。
  • 原子操作:通过 std::atomic 提供的原子操作来避免锁的使用。
示例7:使用 std::atomic 避免竞争条件
#include <iostream>
#include <thread>
#include <atomic>

std::atomic<int> counter(0);  // 原子操作

void incrementCounter() {
    for (int i = 0; i < 1000; ++i) {
        ++counter;  // 原子操作,不需要锁
    }
}

int main() {
    std::thread t1(incrementCounter);
    std::thread t2(incrementCounter);

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

    std::cout << "Final counter value: " << counter << std::endl;
    return 0;
}
解释:
  • std::atomic<int> counter(0);:原子变量 counter,可以在线程之间安全递增,而不需要锁。

8. 线程间通信

线程间通信的常用方法是使用条件变量(std::condition_variable)来让一个线程等待另一个线程的信号。

示例8:使用 std::condition_variable 进行线程间通信
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>

std::mutex mtx;
std::condition_variable cv;
bool ready = false;

void worker() {
    std::unique_lock<std::mutex> lock(mtx);
    cv.wait(lock, [] { return ready; });  // 等待 ready 变为 true
    std::cout << "Worker thread proceeding..." << std::endl;
}

void notify() {
    std::this_thread::sleep_for(std::chrono::seconds(1));  // 模拟其他任务
    {
        std::lock_guard<std::mutex
  • 11
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值