面试题答案
一键面试1. 任务调度机制
- 线程:C++11引入了
<thread>
库,允许创建和管理线程。
#include <iostream>
#include <thread>
void thread_function() {
std::cout << "This is a thread function." << std::endl;
}
int main() {
std::thread t(thread_function);
std::cout << "Main thread is doing other work." << std::endl;
t.join();
return 0;
}
在上述代码中,std::thread t(thread_function);
创建了一个新线程,t.join();
等待该线程完成。
2. 同步机制
- 互斥锁(Mutex):
<mutex>
库提供了互斥锁,用于保护共享资源。
#include <iostream>
#include <mutex>
#include <thread>
std::mutex mtx;
int shared_variable = 0;
void increment() {
for(int i = 0; i < 1000000; ++i) {
mtx.lock();
++shared_variable;
mtx.unlock();
}
}
int main() {
std::thread t1(increment);
std::thread t2(increment);
t1.join();
t2.join();
std::cout << "Final value of shared variable: " << shared_variable << std::endl;
return 0;
}
这里mtx.lock()
和mtx.unlock()
确保在同一时间只有一个线程可以访问shared_variable
。
- 条件变量(Condition Variable):
<condition_variable>
库,用于线程间的通信。
#include <iostream>
#include <condition_variable>
#include <mutex>
#include <thread>
#include <queue>
std::mutex mtx;
std::condition_variable cv;
std::queue<int> data_queue;
bool ready = false;
void producer() {
for(int i = 0; i < 10; ++i) {
std::unique_lock<std::mutex> lock(mtx);
data_queue.push(i);
lock.unlock();
cv.notify_one();
}
ready = true;
cv.notify_all();
}
void consumer() {
while(true) {
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, []{ return!data_queue.empty() || ready; });
if(data_queue.empty() && ready) break;
int value = data_queue.front();
data_queue.pop();
lock.unlock();
std::cout << "Consumed: " << value << std::endl;
}
}
int main() {
std::thread producer_thread(producer);
std::thread consumer_thread(consumer);
producer_thread.join();
consumer_thread.join();
return 0;
}
cv.wait(lock, []{ return!data_queue.empty() || ready; });
等待条件变量满足,这里条件是队列不为空或者生产者已完成。
3. 避免死锁
- 破坏死锁的四个必要条件:
- 互斥条件:很难避免,因为共享资源通常需要互斥访问。
- 占有并等待条件:线程在获取所有需要的资源前不占有任何资源,如采用一次性分配所有资源的策略。
- 不可剥夺条件:操作系统可以通过剥夺资源来避免死锁,但在用户层面较难实现。
- 循环等待条件:对资源进行排序,线程按照相同顺序获取资源。
std::mutex resource1;
std::mutex resource2;
void thread1() {
std::lock(resource1, resource2);
std::unique_lock<std::mutex> lock1(resource1, std::adopt_lock);
std::unique_lock<std::mutex> lock2(resource2, std::adopt_lock);
// 执行需要访问资源1和资源2的代码
}
void thread2() {
std::lock(resource1, resource2);
std::unique_lock<std::mutex> lock1(resource1, std::adopt_lock);
std::unique_lock<std::mutex> lock2(resource2, std::adopt_lock);
// 执行需要访问资源1和资源2的代码
}
std::lock
一次性获取多个锁,避免了循环等待。
4. 提高调度效率
- 减少锁的粒度:只对真正需要保护的资源加锁,而不是对整个数据结构加锁。
class SharedData {
public:
std::mutex mtx1;
std::mutex mtx2;
int data1;
int data2;
void updateData1(int new_value) {
std::unique_lock<std::mutex> lock(mtx1);
data1 = new_value;
}
void updateData2(int new_value) {
std::unique_lock<std::mutex> lock(mtx2);
data2 = new_value;
}
};
这里对data1
和data2
分别加锁,而不是对整个SharedData
对象加锁。
- 使用无锁数据结构:如
std::atomic
类型,在一些场景下可以避免锁的开销。
#include <iostream>
#include <atomic>
#include <thread>
std::atomic<int> atomic_variable(0);
void increment_atomic() {
for(int i = 0; i < 1000000; ++i) {
++atomic_variable;
}
}
int main() {
std::thread t1(increment_atomic);
std::thread t2(increment_atomic);
t1.join();
t2.join();
std::cout << "Final value of atomic variable: " << atomic_variable << std::endl;
return 0;
}
std::atomic
类型的操作是原子的,无需额外的锁。