MST

星途 面试题库

面试题:C++ STL算法sort并行排序中的线程安全与同步问题

在实现C++ STL算法sort的并行排序时,如何确保线程安全和处理同步问题?详细描述可能出现的线程安全问题场景,如数据竞争、死锁等,并说明如何通过锁机制、无锁数据结构或其他同步策略来解决这些问题。同时,请给出一个实际场景下处理线程安全与同步的代码示例,并解释代码的工作原理。
42.4万 热度难度
编程语言C++

知识考点

AI 面试

面试题答案

一键面试

1. 线程安全问题场景

  • 数据竞争:当多个线程同时访问和修改共享数据时,可能会导致数据不一致。例如,在并行排序中,多个线程可能同时尝试修改数组中的元素。
  • 死锁:当两个或多个线程相互等待对方释放资源时,会发生死锁。例如,线程A持有锁1并等待锁2,而线程B持有锁2并等待锁1。

2. 解决方法

  • 锁机制
    • 互斥锁(Mutex):最常用的锁类型。在访问共享数据前,线程必须先获取互斥锁,访问结束后释放。例如:
#include <iostream>
#include <vector>
#include <algorithm>
#include <mutex>
#include <thread>

std::mutex mtx;

void parallel_sort_section(std::vector<int>& data, int start, int end) {
    std::sort(data.begin() + start, data.begin() + end);
}

void parallel_sort(std::vector<int>& data, int num_threads) {
    int size = data.size();
    int chunk_size = size / num_threads;
    std::vector<std::thread> threads;

    for (int i = 0; i < num_threads; ++i) {
        int start = i * chunk_size;
        int end = (i == num_threads - 1)? size : (i + 1) * chunk_size;
        threads.emplace_back(parallel_sort_section, std::ref(data), start, end);
    }

    for (auto& th : threads) {
        if (th.joinable()) {
            th.join();
        }
    }

    // 合并各部分
    for (int i = 1; i < num_threads; ++i) {
        std::vector<int> temp;
        int start1 = 0, end1 = i * chunk_size;
        int start2 = i * chunk_size, end2 = (i == num_threads - 1)? size : (i + 1) * chunk_size;

        std::merge(data.begin() + start1, data.begin() + end1, data.begin() + start2, data.begin() + end2, std::back_inserter(temp));
        mtx.lock();
        std::copy(temp.begin(), temp.end(), data.begin() + start1);
        mtx.unlock();
    }
}
  • 无锁数据结构:使用无锁数据结构(如无锁队列、无锁哈希表)可以避免锁带来的开销。在排序场景中,虽然直接使用无锁数据结构较难,但可以在辅助数据结构(如任务队列)中使用。
  • 其他同步策略
    • 条件变量(Condition Variable):用于线程间的同步通信。例如,当某个线程完成排序任务后,通过条件变量通知其他线程进行合并操作。

3. 代码工作原理

  • parallel_sort_section函数:负责对数组的一个子区间进行排序,这里使用标准库的std::sort
  • parallel_sort函数
    • 首先将数组划分为num_threads个区间,每个区间由一个线程负责排序。
    • 创建线程并启动,每个线程执行parallel_sort_section函数。
    • 等待所有线程完成排序。
    • 然后通过std::merge函数将各个子区间合并,在合并过程中使用互斥锁mtx来保证数据的线程安全,防止在合并时其他线程对数据进行修改。