面试题答案
一键面试引用和指针在多线程场景下的特性
- 引用:
- 特性:引用一旦初始化,就不能再指向其他对象,它是对象的别名。这使得在多线程环境中,如果引用的对象本身是线程安全的,那么使用引用访问该对象相对简单直接,因为不用担心引用被意外修改指向其他对象。
- 缺点:不能为
nullptr
,如果共享资源可能存在空的情况,引用处理起来不如指针灵活。
- 指针:
- 特性:指针可以指向不同的对象,也可以为
nullptr
。这在共享资源可能为空或者需要动态重新分配资源的场景下很有用。但是,指针的灵活性也带来了风险,在多线程环境中,指针可能被意外修改指向错误的对象,导致数据访问错误。
- 特性:指针可以指向不同的对象,也可以为
在不同并发控制策略下的选择
- 互斥锁:
- 使用引用:如果共享资源在程序运行期间不会变为
nullptr
,并且不需要重新分配,使用引用可以简化代码,提高可读性。因为互斥锁保证同一时间只有一个线程能访问共享资源,使用引用不会出现引用指向混乱的问题。 - 使用指针:当共享资源可能为空或者需要动态重新分配时,指针更合适。但是在使用指针时,需要特别注意在获取互斥锁后再操作指针,防止指针在其他线程中被意外修改。
- 使用引用:如果共享资源在程序运行期间不会变为
- 读写锁:
- 使用引用:对于读多写少的场景,如果共享资源不会变为
nullptr
,引用很适合读操作。因为读操作可以多个线程同时进行,引用的稳定性可以保证数据一致性。在写操作时,获取写锁后,也可以使用引用安全地修改共享资源。 - 使用指针:当共享资源的生命周期需要动态管理,或者在写操作时可能需要重新分配内存(导致指针变化),指针更合适。同样,在读写操作时都要正确获取读写锁,防止数据竞争。
- 使用引用:对于读多写少的场景,如果共享资源不会变为
代码示例
- 使用互斥锁和引用:
#include <iostream>
#include <mutex>
#include <thread>
class SharedResource {
public:
int data;
SharedResource(int value) : data(value) {}
};
std::mutex resourceMutex;
void modifyResource(SharedResource& res, int newVal) {
std::lock_guard<std::mutex> lock(resourceMutex);
res.data = newVal;
}
void readResource(const SharedResource& res) {
std::lock_guard<std::mutex> lock(resourceMutex);
std::cout << "Read data: " << res.data << std::endl;
}
int main() {
SharedResource resource(10);
std::thread writer1(modifyResource, std::ref(resource), 20);
std::thread reader1(readResource, std::ref(resource));
writer1.join();
reader1.join();
return 0;
}
解释:这里 SharedResource
类有一个 int
类型的成员变量 data
。modifyResource
函数通过引用修改共享资源,readResource
函数通过引用读取共享资源。在这两个函数中,都使用 std::lock_guard
来获取互斥锁,保证线程安全。
- 使用读写锁和指针:
#include <iostream>
#include <shared_mutex>
#include <thread>
#include <memory>
class SharedResource {
public:
int data;
SharedResource(int value) : data(value) {}
};
std::shared_mutex resourceMutex;
std::unique_ptr<SharedResource> resourcePtr;
void writeResource(int newVal) {
std::unique_lock<std::shared_mutex> lock(resourceMutex);
if (!resourcePtr) {
resourcePtr = std::make_unique<SharedResource>(newVal);
} else {
resourcePtr->data = newVal;
}
}
void readResource() {
std::shared_lock<std::shared_mutex> lock(resourceMutex);
if (resourcePtr) {
std::cout << "Read data: " << resourcePtr->data << std::endl;
} else {
std::cout << "Resource is nullptr" << std::endl;
}
}
int main() {
std::thread writer1(writeResource, 30);
std::thread reader1(readResource);
writer1.join();
reader1.join();
return 0;
}
解释:这里使用 std::shared_mutex
作为读写锁。writeResource
函数在获取独占锁(std::unique_lock
)后,可以动态分配或修改 SharedResource
对象。readResource
函数获取共享锁(std::shared_lock
)后读取共享资源。通过指针 resourcePtr
可以处理共享资源可能为空的情况,在多线程读写操作中保证数据的一致性和安全性。