非虚函数在多线程编程中的挑战
- 资源竞争
- 当多个线程同时调用非虚函数且函数中涉及共享资源(如全局变量、静态成员变量)的读写操作时,就会产生资源竞争。例如:
int globalVar = 0;
void nonVirtualFunction() {
globalVar++;
}
- 这里
globalVar
是共享资源,多个线程同时调用nonVirtualFunction
时,globalVar++
不是原子操作,可能导致结果不准确。
- 线程安全
- 非虚函数本身不会自动处理线程安全问题。如果函数中包含对共享资源的复杂操作序列,如多个相关的读写操作,没有适当的同步机制,就可能出现数据不一致的情况。例如:
class MyClass {
public:
void nonVirtualMethod() {
int local = sharedValue;
// 其他操作
local++;
sharedValue = local;
}
private:
int sharedValue;
};
- 在多线程环境下,不同线程执行上述
nonVirtualMethod
时,由于没有同步,可能导致sharedValue
的值出现错误。
多线程场景下安全且高效使用非虚函数的方法
- 使用互斥锁(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;
}
- 使用原子操作
- 对于简单的共享资源操作,如整数的增减,可以使用原子类型。例如:
#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;
}
设计模式建议
- 单例模式(线程安全版本)
- 在多线程环境下实现单例模式时,非虚函数的使用要确保线程安全。一种常见的方法是使用双重检查锁定(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;
};
- 生产者 - 消费者模式
- 可以通过队列作为共享资源,生产者线程调用非虚函数向队列中添加数据,消费者线程调用非虚函数从队列中取出数据。使用互斥锁和条件变量来保证线程安全和同步。
#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;
}