MST

星途 面试题库

面试题:并发与同步之操作系统同步原语组合的基础应用

在一个多线程的程序中,有两个线程:线程A负责向共享缓冲区写入数据,线程B负责从共享缓冲区读取数据。请使用信号量和互斥锁这两种同步原语,设计一个方案来保证数据的正确读写,防止竞态条件。请描述你的设计思路,并给出关键代码片段(语言不限)。
47.2万 热度难度
操作系统并发与同步

知识考点

AI 面试

面试题答案

一键面试

设计思路

  1. 互斥锁(Mutex):用于保护共享缓冲区,确保在同一时间只有一个线程可以访问共享缓冲区,防止多个线程同时读写造成的数据不一致。
  2. 信号量(Semaphore)
    • 一个信号量(例如 empty)用于表示共享缓冲区中的空闲空间,初始值为缓冲区的容量。当线程A要写入数据时,先获取 empty 信号量,如果有空闲空间则可以写入,写入后释放表示有数据的信号量(例如 full)。
    • 另一个信号量(例如 full)用于表示共享缓冲区中的数据数量,初始值为0。当线程B要读取数据时,先获取 full 信号量,如果有数据则可以读取,读取后释放 empty 信号量。

关键代码片段(以C++为例,使用C++11的线程库和信号量、互斥锁相关类)

#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <queue>
#include <chrono>

std::mutex mtx;
std::condition_variable empty, full;
std::queue<int> sharedBuffer;
const int bufferSize = 5;

void threadA() {
    for (int i = 0; i < 10; ++i) {
        std::unique_lock<std::mutex> lock(mtx);
        empty.wait(lock, [] { return sharedBuffer.size() < bufferSize; });
        sharedBuffer.push(i);
        std::cout << "Thread A pushed: " << i << std::endl;
        lock.unlock();
        full.notify_one();
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
    }
}

void threadB() {
    for (int i = 0; i < 10; ++i) {
        std::unique_lock<std::mutex> lock(mtx);
        full.wait(lock, [] { return!sharedBuffer.empty(); });
        int data = sharedBuffer.front();
        sharedBuffer.pop();
        std::cout << "Thread B popped: " << data << std::endl;
        lock.unlock();
        empty.notify_one();
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
    }
}

int main() {
    std::thread a(threadA);
    std::thread b(threadB);

    a.join();
    b.join();

    return 0;
}

在上述代码中:

  • std::mutex mtx 是互斥锁,用于保护共享缓冲区 sharedBuffer
  • std::condition_variable emptystd::condition_variable full 模拟信号量的功能,empty 表示缓冲区的空闲空间,full 表示缓冲区中的数据。
  • threadA 线程向共享缓冲区写入数据,threadB 线程从共享缓冲区读取数据,通过互斥锁和条件变量(模拟信号量)保证了数据的正确读写,避免了竞态条件。