并行编程和并发

1. 概述

并行编程和并发是现代软件开发中的重要概念,尤其是在处理大规模数据和高性能计算时。并行编程指的是同时执行多个计算任务,而并发则是指多个任务在同一时间段内交替执行。C++ 提供了丰富的工具和库来支持并行编程和并发,使得开发者能够充分利用多核处理器的性能。

2. 理论基础

2.1 并行与并发的区别

  • 并行(Parallelism):多个任务在同一时刻同时执行。通常用于多核处理器或多台计算机上。
  • 并发(Concurrency):多个任务在同一时间段内交替执行。即使只有一个处理器,也可以通过任务切换实现并发。

2.2 线程与进程

  • 进程(Process):操作系统中资源分配的基本单位,每个进程有独立的内存空间。
  • 线程(Thread):进程中的执行单元,多个线程共享同一进程的内存空间。

3. C++ 中的并行编程

3.1 std::thread 库

C++11 引入了 std::thread 库,使得线程管理变得更加简单和安全。

3.1.1 创建线程

#include <iostream>
#include <thread>

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

int main() {
    std::thread t(hello);
    t.join(); // 等待线程完成
    std::cout << "Main thread exiting." << std::endl;
    return 0;
}

3.1.2 传递参数

#include <iostream>
#include <thread>

void greet(const std::string& name) {
    std::cout << "Hello, " << name << "!" << std::endl;
}

int main() {
    std::thread t(greet, "Alice");
    t.join();
    std::cout << "Main thread exiting." << std::endl;
    return 0;
}

3.2 线程同步

在多线程编程中,线程同步是确保多个线程正确协作的关键。C++ 提供了多种同步机制,如 std::mutex、std::lock_guard 和 std::condition_variable。

3.2.1 std::mutex

#include <iostream>
#include <thread>
#include <mutex>

std::mutex mtx;

void increment(int& counter) {
    for (int i = 0; i < 100000; ++i) {
        std::lock_guard<std::mutex> lock(mtx);
        ++counter;
    }
}

int main() {
    int counter = 0;
    std::thread t1(increment, std::ref(counter));
    std::thread t2(increment, std::ref(counter));
    t1.join();
    t2.join();
    std::cout << "Counter: " << counter << std::endl;
    return 0;
}

3.2.2 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; });
    std::cout << "Worker thread is processing data." << std::endl;
}

void prepare() {
    std::this_thread::sleep_for(std::chrono::seconds(1));
    std::cout << "Main thread is preparing data." << std::endl;
    {
        std::lock_guard<std::mutex> lock(mtx);
        ready = true;
    }
    cv.notify_one();
}

int main() {
    std::thread t1(worker);
    std::thread t2(prepare);
    t1.join();
    t2.join();
    return 0;
}

4. 实践练习

4.1 计算斐波那契数列

编写一个程序,使用多线程计算斐波那契数列的前 N 项。

#include <iostream>
#include <thread>
#include <vector>

void fibonacci(int n, std::vector<int>& result) {
    result.resize(n);
    result[0] = 0;
    if (n > 1) result[1] = 1;
    for (int i = 2; i < n; ++i) {
        result[i] = result[i-1] + result[i-2];
    }
}

int main() {
    const int N = 20;
    std::vector<int> result;
    std::thread t(fibonacci, N, std::ref(result));
    t.join();
    for (int num : result) {
        std::cout << num << " ";
    }
    std::cout << std::endl;
    return 0;
}

4.2 生产者-消费者问题

实现一个简单的生产者-消费者模型,使用 std::mutex 和 std::condition_variable 进行同步。

#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <queue>

std::mutex mtx;
std::condition_variable cv;
std::queue<int> buffer;
const int BUFFER_SIZE = 10;

void producer() {
    for (int i = 0; i < 20; ++i) {
        std::unique_lock<std::mutex> lock(mtx);
        cv.wait(lock, []{ return buffer.size() < BUFFER_SIZE; });
        buffer.push(i);
        std::cout << "Produced: " << i << std::endl;
        lock.unlock();
        cv.notify_one();
    }
}

void consumer() {
    for (int i = 0; i < 20; ++i) {
        std::unique_lock<std::mutex> lock(mtx);
        cv.wait(lock, []{ return !buffer.empty(); });
        int value = buffer.front();
        buffer.pop();
        std::cout << "Consumed: " << value << std::endl;
        lock.unlock();
        cv.notify_one();
    }
}

int main() {
    std::thread t1(producer);
    std::thread t2(consumer);
    t1.join();
    t2.join();
    return 0;
}

5. 总结

并行编程和并发是现代软件开发中的重要技能。C++ 提供了强大的工具和库来支持这些技术,使得开发者能够编写高效、可靠的多线程程序。通过本教程的学习,你应该能够理解并行编程的基本概念,并能够使用 C++ 编写简单的多线程程序。

6. 进一步学习

  • 深入学习 C++ 标准库中的并发工具,如 std::async 和 std::future。
  • 探索更高级的并发模式,如读写锁(std::shared_mutex)和无锁数据结构。
  • 学习如何使用并行算法库(如 std::execution)来优化数据处理任务。

通过不断的实践和学习,你将能够掌握并行编程和并发技术,并在实际项目中应用这些知识。