MST

星途 面试题库

面试题:C++类成员回调函数在多线程环境下的性能优化与同步机制

考虑一个多线程应用程序,其中类成员回调函数会在不同线程中被调用。为了保证数据一致性和提高性能,你需要设计一套同步机制。请详细描述你将如何设计该同步机制,包括选择哪些同步原语(如互斥锁、条件变量、信号量等),如何处理死锁问题,以及这些同步操作对整体性能的影响,并且结合代码示例说明如何实现。
33.3万 热度难度
编程语言C++

知识考点

AI 面试

面试题答案

一键面试

同步机制设计

  1. 同步原语选择

    • 互斥锁(Mutex):用于保护共享数据,确保在同一时间只有一个线程能够访问这些数据。例如,当多个线程可能同时访问和修改类的某个成员变量时,在访问该变量前后加锁解锁。
    • 条件变量(Condition Variable):用于线程间的通信和同步。当某个条件满足时,通过条件变量通知等待的线程。比如,在生产者 - 消费者模型中,当缓冲区有数据时,生产者线程通过条件变量通知消费者线程。
    • 信号量(Semaphore):可以控制同时访问共享资源的线程数量。如果共享资源有一定的数量限制,例如数据库连接池的连接数量有限,就可以使用信号量来管理。
  2. 处理死锁问题

    • 破坏死锁的四个必要条件
      • 互斥条件:无法完全破坏,因为共享数据必须互斥访问。
      • 占有并等待条件:可以要求线程一次性获取所有需要的锁,而不是获取部分锁后再等待其他锁。例如,在进入某个关键代码段前,先获取所有相关的锁。
      • 不可剥夺条件:当检测到死锁时,可以通过某种机制剥夺某个线程的锁资源,让其他线程能够继续执行。例如,在操作系统层面可以通过优先级等方式剥夺锁。
      • 循环等待条件:对锁进行排序,要求线程按照固定顺序获取锁。例如,为所有锁分配一个唯一的ID,线程总是先获取ID小的锁,再获取ID大的锁。
    • 死锁检测:定期检查线程的锁持有情况和等待状态,当发现有循环等待的情况时,采取相应措施,如终止某个线程或者释放部分锁资源。
  3. 同步操作对整体性能的影响

    • 积极影响:通过同步机制保证了数据一致性,避免了数据竞争导致的未定义行为和错误结果,提高了程序的稳定性和正确性。这在一些对数据准确性要求极高的场景,如金融计算等非常重要。
    • 消极影响:同步操作会引入额外的开销,例如加锁解锁操作需要时间,可能导致线程上下文切换等。过多的同步操作会降低并行度,使程序的性能瓶颈出现在同步操作上,降低整体执行效率。

代码示例(以C++为例)

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

class SharedData {
public:
    void addData(int value) {
        std::unique_lock<std::mutex> lock(mutex_);
        dataQueue.push(value);
        lock.unlock();
        condVar.notify_one();
    }

    bool getData(int& value) {
        std::unique_lock<std::mutex> lock(mutex_);
        condVar.wait(lock, [this] { return!dataQueue.empty(); });
        if (dataQueue.empty()) {
            return false;
        }
        value = dataQueue.front();
        dataQueue.pop();
        return true;
    }

private:
    std::queue<int> dataQueue;
    std::mutex mutex_;
    std::condition_variable condVar;
};

void producer(SharedData& sharedData) {
    for (int i = 0; i < 10; ++i) {
        sharedData.addData(i);
        std::cout << "Produced: " << i << std::endl;
    }
}

void consumer(SharedData& sharedData) {
    int value;
    while (true) {
        if (sharedData.getData(value)) {
            std::cout << "Consumed: " << value << std::endl;
        } else {
            std::this_thread::sleep_for(std::chrono::milliseconds(100));
        }
    }
}

int main() {
    SharedData sharedData;
    std::thread producerThread(producer, std::ref(sharedData));
    std::thread consumerThread(consumer, std::ref(sharedData));

    producerThread.join();
    consumerThread.join();

    return 0;
}

在上述代码中:

  • SharedData类包含一个队列dataQueue用于存储数据,一个互斥锁mutex_保护队列,一个条件变量condVar用于线程间通信。
  • addData函数在向队列添加数据前加锁,添加完成后解锁并通知等待的线程。
  • getData函数使用条件变量等待队列有数据,获取数据前加锁,获取后解锁。
  • producer线程模拟生产者向共享数据添加数据,consumer线程模拟消费者从共享数据获取数据。