MST

星途 面试题库

面试题:C++实时系统的任务调度与同步机制

在C++实时系统里,任务调度和同步对于系统的正确性和性能影响极大。请详细描述C++中可以用于实现任务调度(如线程调度)和同步(如互斥锁、条件变量)的机制,同时分析如何避免死锁以及提高调度效率,结合实际代码示例说明。
18.0万 热度难度
编程语言C++

知识考点

AI 面试

面试题答案

一键面试

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;
    }
};

这里对data1data2分别加锁,而不是对整个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类型的操作是原子的,无需额外的锁。