聊一下你对并发编程的基本理解

63 阅读4分钟

并发编程的基本理解 并发与并行的区别 **并发(Concurrency)**指的是多个任务在同一时间段内交替执行,CPU通过快速切换任务,使得多个任务“看似同时”执行,但实际上在单核CPU上是时间片轮转的逻辑同时执行。例如,一个CPU核心上多个线程交替运行,每个线程执行一小段时间后切换到另一个线程。

**并行(Parallelism)**是真正意义上的同时执行,多个任务在多个CPU核心上同时运行,适用于多核处理器环境。

并发是解决程序阻塞和提升资源利用率的手段,适合I/O密集型任务;并行则是利用多核CPU加速计算密集型任务。

进程与线程 进程是操作系统资源分配的基本单位,拥有独立的内存空间和系统资源。

线程是进程中的执行单元,多个线程共享进程的资源和内存空间,线程间通信更高效但也更复杂。

并发编程通常指多线程编程,即在一个进程内管理多个线程实现任务的并发执行。

C++中的并发编程支持 自C++11起,C++标准库引入了对并发编程的原生支持,主要包括:

std::thread:用于创建和管理线程。

std::mutex:互斥量,用于保护共享资源,防止数据竞争。

std::condition_variable:条件变量,实现线程间同步和通信。

std::atomic:原子操作,保证对共享数据的操作不可中断,避免竞态条件。

std::future/std::promise:支持异步任务和结果获取。

这些工具为C++开发者提供了构建线程安全并发程序的基础设施。

并发编程面临的主要挑战 数据竞争(Race Condition):多个线程同时访问共享资源且至少有一个线程修改资源,导致数据不一致。

死锁(Deadlock):多个线程互相等待对方释放资源,导致程序无法继续执行。

线程安全:确保共享资源在多线程环境下访问的正确性和一致性。

线程同步与通信:合理设计线程间的协调机制,保证执行顺序和数据正确传递。

举例说明并发编程

  1. 基本线程创建与执行
#include <iostream>
#include <thread>

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

int main() {
    std::thread t(hello);  // 创建线程执行hello函数
    t.join();              // 等待线程执行完毕
    return 0;
}

这个例子展示了如何创建一个线程并等待其完成,线程执行一个简单的打印任务。

  1. 互斥锁保护共享资源
#include <iostream>
#include <thread>
#include <mutex>

std::mutex mtx;
int counter = 0;

void increment() {
    for (int i = 0; i < 10000; ++i) {
        std::lock_guard<std::mutex> lock(mtx); // 自动加锁解锁
        ++counter;
    }
}

int main() {
    std::thread t1(increment);
    std::thread t2(increment);
    t1.join();
    t2.join();
    std::cout << "Counter: " << counter << std::endl; // 结果应为20000
    return 0;
}

通过std::mutex和std::lock_guard保护共享变量counter,避免竞态条件,确保结果正确。

  1. 条件变量实现生产者-消费者模型
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <queue>

std::mutex mtx;
std::condition_variable cv;
std::queue<int> dataQueue;
bool finished = false;

void producer() {
    for (int i = 0; i < 10; ++i) {
        {
            std::lock_guard<std::mutex> lock(mtx);
            dataQueue.push(i);
            std::cout << "Produced: " << i << std::endl;
        }
        cv.notify_one(); // 通知消费者
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
    }
    {
        std::lock_guard<std::mutex> lock(mtx);
        finished = true;
    }
    cv.notify_one();
}

void consumer() {
    while (true) {
        std::unique_lock<std::mutex> lock(mtx);
        cv.wait(lock, [] { return !dataQueue.empty() || finished; });
        while (!dataQueue.empty()) {
            int val = dataQueue.front();
            dataQueue.pop();
            std::cout << "Consumed: " << val << std::endl;
        }
        if (finished) break;
    }
}

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

生产者线程生产数据并通知消费者,消费者等待条件变量,保证线程同步和数据安全。

  1. 使用std::async实现异步任务
#include <iostream>
#include <future>

int compute() {
    return 42;
}

int main() {
    std::future<int> fut = std::async(std::launch::async, compute);
    std::cout << "Result: " << fut.get() << std::endl;
    return 0;
}

std::async启动异步任务,返回std::future用于获取结果,简化异步编程。

并发编程的面试要点总结 理解并发与并行的区别及适用场景。

熟悉C++11及以上标准库中线程、互斥量、条件变量、原子操作等工具的使用。

能够识别和解决竞态条件、死锁等并发问题。

掌握线程同步机制,如互斥锁、条件变量的使用。

具备设计生产者消费者、线程池等并发模型的能力。

理解线程生命周期管理,合理使用join和detach。

了解C++智能指针(如shared_ptr)在多线程环境中的线程安全问题及加锁保护。

综上所述,并发编程是指在单个系统中通过多线程或多进程实现多个任务在时间上的交替执行(逻辑上的同时进行),以提升程序的响应性和效率。C++通过标准库提供了丰富的并发编程支持,开发者需要掌握线程创建、同步、通信、资源保护等技术,并能够结合具体业务场景合理设计并发方案。通过上述示例和说明,我相信可以帮助面试官清晰理解我对并发编程的深刻理解和实战能力。