MST
星途 面试题库

面试题:C++ 多线程环境下类成员回调函数的参数传递与线程安全

在多线程环境中,有一个 `WorkerClass` 类,其中的成员函数 `workerCallback` 作为回调函数。不同的线程会频繁触发这个回调函数,并传递不同类型的参数(如 `std::vector<int>`、自定义类对象等)。请设计一个方案,确保在多线程并发访问时,参数传递的正确性和线程安全性,避免数据竞争。要求写出完整的类定义、回调函数实现,以及多线程触发回调的代码示例,并详细说明你采取的线程安全措施及其原理。
34.6万 热度难度
编程语言C++

知识考点

AI 面试

面试题答案

一键面试

1. 类定义及回调函数实现

#include <iostream>
#include <vector>
#include <mutex>
#include <thread>
#include <functional>
#include <queue>

// 自定义类
class CustomClass {
public:
    int data;
    CustomClass(int d) : data(d) {}
};

class WorkerClass {
private:
    std::mutex mtx;
    std::queue<std::function<void()>> taskQueue;

public:
    // 回调函数,处理不同类型参数
    template <typename... Args>
    void workerCallback(Args&&... args) {
        std::unique_lock<std::mutex> lock(mtx);
        taskQueue.push([&]() {
            // 这里处理实际的参数逻辑
            (void)std::initializer_list<int>{((void)std::cout << "处理参数: ", 0)...};
        });
    }

    // 模拟多线程触发回调的执行函数
    void executeTasks() {
        while (true) {
            std::function<void()> task;
            {
                std::unique_lock<std::mutex> lock(mtx);
                if (taskQueue.empty()) break;
                task = std::move(taskQueue.front());
                taskQueue.pop();
            }
            task();
        }
    }
};

2. 多线程触发回调的代码示例

int main() {
    WorkerClass worker;
    std::vector<std::thread> threads;

    // 模拟多个线程触发回调
    for (int i = 0; i < 5; ++i) {
        threads.emplace_back([&worker, i]() {
            if (i % 2 == 0) {
                std::vector<int> vec = {1, 2, 3};
                worker.workerCallback(vec);
            } else {
                CustomClass obj(i);
                worker.workerCallback(obj);
            }
        });
    }

    std::thread executor(&WorkerClass::executeTasks, &worker);

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

    // 等待任务队列处理完毕
    executor.join();

    return 0;
}

3. 线程安全措施及其原理

  • 互斥锁(std::mutex:在 WorkerClass 类中定义了一个 std::mutex 成员变量 mtx。在 workerCallback 函数中,使用 std::unique_lock<std::mutex> 来锁定互斥锁 mtx。这确保了在多线程环境下,对 taskQueue 的操作(如入队操作)是线程安全的。因为同一时间只有一个线程能够获取到锁,其他试图获取锁的线程会被阻塞,从而避免了数据竞争。
  • 任务队列(std::queue<std::function<void()>>:通过将不同类型参数的处理逻辑封装成 std::function<void()> 放入任务队列 taskQueue 中。这样做的好处是,在多线程触发回调时,只需要将任务放入队列,而实际的处理逻辑在一个单独的线程(这里通过 executeTasks 函数模拟)中按顺序执行,进一步避免了多线程并发处理参数时可能产生的数据竞争。同时,executeTasks 函数在从任务队列中取出任务时,也使用了互斥锁来保证线程安全。