MST

星途 面试题库

面试题:C++多线程环境下对象间数据共享的同步机制及优化

在C++多线程编程中,多个线程可能访问共享对象的数据。请详细说明如何确保数据共享的正确性,涉及到哪些同步机制(如互斥锁、条件变量、原子操作等)。并进一步阐述如何对这些同步机制进行优化,以提高多线程程序在对象间数据共享时的性能,结合实际代码示例说明。
23.5万 热度难度
编程语言C++

知识考点

AI 面试

面试题答案

一键面试

确保数据共享正确性的方法及同步机制

  1. 互斥锁(Mutex)
    • 原理:互斥锁用于保护共享资源,同一时间只有一个线程可以获取锁,从而访问共享数据。获取锁的线程执行完对共享数据的操作后,释放锁,其他线程才有机会获取锁并访问数据。
    • 示例代码
#include <iostream>
#include <mutex>
#include <thread>

std::mutex mtx;
int sharedData = 0;

void increment() {
    for (int i = 0; i < 10000; ++i) {
        mtx.lock();
        sharedData++;
        mtx.unlock();
    }
}

int main() {
    std::thread t1(increment);
    std::thread t2(increment);

    t1.join();
    t2.join();

    std::cout << "Final value of sharedData: " << sharedData << std::endl;
    return 0;
}
  1. 条件变量(Condition Variable)
    • 原理:条件变量通常与互斥锁一起使用,用于线程间的同步通信。一个线程可以在条件变量上等待,直到另一个线程通知该条件变量,此时等待的线程被唤醒。
    • 示例代码
#include <iostream>
#include <mutex>
#include <condition_variable>
#include <thread>

std::mutex mtx;
std::condition_variable cv;
bool ready = false;

void print_id(int id) {
    std::unique_lock<std::mutex> lock(mtx);
    while (!ready) cv.wait(lock);
    std::cout << "thread " << id << '\n';
}

void go() {
    std::unique_lock<std::mutex> lock(mtx);
    ready = true;
    cv.notify_all();
}

int main() {
    std::thread threads[10];
    for (int i = 0; i < 10; ++i)
        threads[i] = std::thread(print_id, i);

    std::cout << "10 threads ready to race...\n";
    go();

    for (auto& th : threads) th.join();

    return 0;
}
  1. 原子操作(Atomic Operations)
    • 原理:原子操作是不可分割的操作,在执行过程中不会被其他线程打断。对于一些简单的数据类型(如整数、指针等),使用原子操作可以避免使用锁带来的开销。
    • 示例代码
#include <iostream>
#include <atomic>
#include <thread>

std::atomic<int> sharedAtomicData(0);

void atomicIncrement() {
    for (int i = 0; i < 10000; ++i) {
        sharedAtomicData++;
    }
}

int main() {
    std::thread t1(atomicIncrement);
    std::thread t2(atomicIncrement);

    t1.join();
    t2.join();

    std::cout << "Final value of sharedAtomicData: " << sharedAtomicData << std::endl;
    return 0;
}

同步机制的优化

  1. 减少锁的粒度
    • 方法:将大的锁保护区域划分成多个小的锁保护区域,使得不同线程可以同时访问不同的共享资源,从而提高并发性能。
    • 示例:假设一个类有多个成员变量,原来使用一个大锁保护所有成员变量的访问,优化后可以为不同成员变量或相关成员变量组分别设置锁。
class SharedResources {
public:
    std::mutex mtx1;
    std::mutex mtx2;
    int data1;
    int data2;

    void updateData1(int value) {
        std::lock_guard<std::mutex> lock(mtx1);
        data1 = value;
    }

    void updateData2(int value) {
        std::lock_guard<std::mutex> lock(mtx2);
        data2 = value;
    }
};
  1. 使用读写锁(Read - Write Lock)
    • 方法:如果共享数据的读操作远多于写操作,可以使用读写锁。读写锁允许多个线程同时进行读操作,但只允许一个线程进行写操作。当有写操作时,读操作会被阻塞。
    • 示例代码
#include <iostream>
#include <shared_mutex>
#include <thread>

std::shared_mutex rwMutex;
int sharedReadWriteData = 0;

void readData() {
    std::shared_lock<std::shared_mutex> lock(rwMutex);
    std::cout << "Read data: " << sharedReadWriteData << std::endl;
}

void writeData(int value) {
    std::unique_lock<std::shared_mutex> lock(rwMutex);
    sharedReadWriteData = value;
    std::cout << "Write data: " << sharedReadWriteData << std::endl;
}

int main() {
    std::thread readThread1(readData);
    std::thread readThread2(readData);
    std::thread writeThread(writeData, 42);

    readThread1.join();
    readThread2.join();
    writeThread.join();

    return 0;
}
  1. 无锁数据结构
    • 方法:使用无锁数据结构,如无锁队列、无锁链表等。这些数据结构通过原子操作和其他技巧实现线程安全,避免了锁带来的开销。
    • 示例:虽然C++标准库没有提供通用的无锁队列,但可以自己实现或使用第三方库(如Intel TBB库中的无锁队列)。例如,自己实现一个简单的无锁队列可以参考以下伪代码思路:
template <typename T>
class LockFreeQueue {
private:
    struct Node {
        T data;
        std::atomic<Node*> next;
        Node(const T& value) : data(value), next(nullptr) {}
    };
    std::atomic<Node*> head;
    std::atomic<Node*> tail;

public:
    LockFreeQueue() : head(new Node(T())), tail(head.load()) {}

    void enqueue(const T& value) {
        Node* newNode = new Node(value);
        Node* oldTail;
        do {
            oldTail = tail.load();
        } while (!tail.compare_exchange_weak(oldTail, newNode));
        oldTail->next.store(newNode);
    }

    bool dequeue(T& value) {
        Node* oldHead;
        Node* next;
        do {
            oldHead = head.load();
            if (oldHead == tail.load()) return false;
            next = oldHead->next.load();
        } while (!head.compare_exchange_weak(oldHead, next));
        value = next->data;
        delete oldHead;
        return true;
    }
};

通过合理选择和优化同步机制,可以有效提高多线程程序在对象间数据共享时的性能。