MST

星途 面试题库

面试题:C++非虚函数在多线程环境下的应用及挑战

在多线程编程的C++环境中,非虚函数的使用可能会带来哪些挑战?例如在资源竞争、线程安全方面。请详细分析,并说明如何在多线程场景下安全且高效地使用非虚函数,同时给出相应的代码示例和设计模式建议。
25.9万 热度难度
编程语言C++

知识考点

AI 面试

面试题答案

一键面试

非虚函数在多线程编程中的挑战

  1. 资源竞争
    • 当多个线程同时调用非虚函数且函数中涉及共享资源(如全局变量、静态成员变量)的读写操作时,就会产生资源竞争。例如:
int globalVar = 0;
void nonVirtualFunction() {
    globalVar++;
}
  • 这里globalVar是共享资源,多个线程同时调用nonVirtualFunction时,globalVar++不是原子操作,可能导致结果不准确。
  1. 线程安全
    • 非虚函数本身不会自动处理线程安全问题。如果函数中包含对共享资源的复杂操作序列,如多个相关的读写操作,没有适当的同步机制,就可能出现数据不一致的情况。例如:
class MyClass {
public:
    void nonVirtualMethod() {
        int local = sharedValue;
        // 其他操作
        local++;
        sharedValue = local;
    }
private:
    int sharedValue;
};
  • 在多线程环境下,不同线程执行上述nonVirtualMethod时,由于没有同步,可能导致sharedValue的值出现错误。

多线程场景下安全且高效使用非虚函数的方法

  1. 使用互斥锁(Mutex)
    • 互斥锁可以保证在同一时间只有一个线程能够进入临界区(访问共享资源的代码段)。例如:
#include <iostream>
#include <mutex>
#include <thread>

std::mutex mtx;
int globalVar = 0;

void nonVirtualFunction() {
    std::lock_guard<std::mutex> lock(mtx);
    globalVar++;
    std::cout << "Thread " << std::this_thread::get_id() << " incremented globalVar to " << globalVar << std::endl;
}

int main() {
    std::thread threads[10];
    for (int i = 0; i < 10; i++) {
        threads[i] = std::thread(nonVirtualFunction);
    }
    for (auto& th : threads) {
        th.join();
    }
    return 0;
}
  1. 使用原子操作
    • 对于简单的共享资源操作,如整数的增减,可以使用原子类型。例如:
#include <iostream>
#include <atomic>
#include <thread>

std::atomic<int> atomicVar(0);

void nonVirtualFunction() {
    atomicVar++;
    std::cout << "Thread " << std::this_thread::get_id() << " incremented atomicVar to " << atomicVar << std::endl;
}

int main() {
    std::thread threads[10];
    for (int i = 0; i < 10; i++) {
        threads[i] = std::thread(nonVirtualFunction);
    }
    for (auto& th : threads) {
        th.join();
    }
    return 0;
}

设计模式建议

  1. 单例模式(线程安全版本)
    • 在多线程环境下实现单例模式时,非虚函数的使用要确保线程安全。一种常见的方法是使用双重检查锁定(Double - Checked Locking),结合互斥锁和静态局部变量(C++11起保证线程安全初始化)。
#include <iostream>
#include <mutex>

class Singleton {
public:
    static Singleton& getInstance() {
        static Singleton instance;
        return instance;
    }
    void nonVirtualMethod() {
        std::cout << "Singleton non - virtual method" << std::endl;
    }
private:
    Singleton() = default;
    ~Singleton() = default;
    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;
};
  1. 生产者 - 消费者模式
    • 可以通过队列作为共享资源,生产者线程调用非虚函数向队列中添加数据,消费者线程调用非虚函数从队列中取出数据。使用互斥锁和条件变量来保证线程安全和同步。
#include <iostream>
#include <queue>
#include <thread>
#include <mutex>
#include <condition_variable>

std::queue<int> dataQueue;
std::mutex queueMutex;
std::condition_variable queueCond;
bool finished = false;

void producer(int id) {
    for (int i = 0; i < 10; i++) {
        std::unique_lock<std::mutex> lock(queueMutex);
        dataQueue.push(id * 10 + i);
        std::cout << "Producer " << id << " added " << id * 10 + i << std::endl;
        lock.unlock();
        queueCond.notify_one();
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
    }
    std::unique_lock<std::mutex> lock(queueMutex);
    finished = true;
    lock.unlock();
    queueCond.notify_all();
}

void consumer() {
    while (true) {
        std::unique_lock<std::mutex> lock(queueMutex);
        queueCond.wait(lock, [] { return!dataQueue.empty() || finished; });
        if (dataQueue.empty() && finished) {
            break;
        }
        int data = dataQueue.front();
        dataQueue.pop();
        std::cout << "Consumer consumed " << data << std::endl;
        lock.unlock();
    }
}
  • main函数中可以启动多个生产者和消费者线程来演示该模式。
int main() {
    std::thread producer1(producer, 1);
    std::thread producer2(producer, 2);
    std::thread consumerThread(consumer);
    producer1.join();
    producer2.join();
    consumerThread.join();
    return 0;
}