MST
星途 面试题库

面试题:C++ 引用传递在多线程环境下的隐患及应对策略

在多线程编程中,当使用引用传递作为函数参数时,可能会出现哪些隐患?例如资源竞争、数据一致性等问题。请详细阐述这些问题产生的原因,并给出至少两种有效的应对策略,包括相应的代码示例。
19.9万 热度难度
编程语言C++

知识考点

AI 面试

面试题答案

一键面试

隐患及原因

  1. 资源竞争
    • 原因:多个线程同时访问并修改通过引用传递的共享资源。例如,多个线程可能同时调用一个函数,该函数接收一个共享数据结构的引用,并对其进行写操作。由于线程执行的不确定性,可能导致数据修改顺序混乱,从而产生资源竞争问题。
    • 示例
import threading

shared_list = []


def append_number_ref(num):
    shared_list.append(num)


threads = []
for i in range(10):
    t = threading.Thread(target = append_number_ref, args=(i,))
    threads.append(t)
    t.start()

for t in threads:
    t.join()

print(shared_list)

在这个例子中,如果多个线程同时执行append_number_ref函数,由于线程执行的不确定性,shared_list的最终结果可能并非按顺序添加元素,这就是资源竞争的表现。

  1. 数据一致性
    • 原因:多个线程对共享资源(通过引用传递)的读写操作没有正确同步。当一个线程正在修改共享数据,而另一个线程同时读取该数据时,可能读到不一致的数据状态。
    • 示例
class Data {
    int value;
}

public class DataConsistencyProblem {
    public static void main(String[] args) {
        Data data = new Data();
        Thread writerThread = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                data.value = i;
            }
        });
        Thread readerThread = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                System.out.println("Read value: " + data.value);
            }
        });
        writerThread.start();
        readerThread.start();
    }
}

在这个Java代码中,readerThread可能会读到data.value的不一致值,因为writerThreaddata.value的修改和readerThread的读取没有同步。

应对策略

  1. 使用锁机制
    • 原理:通过加锁,确保在同一时间只有一个线程能够访问共享资源,从而避免资源竞争和数据不一致问题。
    • Python示例
import threading

shared_list = []
lock = threading.Lock()


def append_number_ref(num):
    with lock:
        shared_list.append(num)


threads = []
for i in range(10):
    t = threading.Thread(target = append_number_ref, args=(i,))
    threads.append(t)
    t.start()

for t in threads:
    t.join()

print(shared_list)
  • Java示例
class Data {
    int value;
    final Object lock = new Object();
}

public class DataConsistencySolution {
    public static void main(String[] args) {
        Data data = new Data();
        Thread writerThread = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                synchronized (data.lock) {
                    data.value = i;
                }
            }
        });
        Thread readerThread = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                synchronized (data.lock) {
                    System.out.println("Read value: " + data.value);
                }
            }
        });
        writerThread.start();
        readerThread.start();
    }
}
  1. 使用线程本地存储(Thread - Local Storage)
    • 原理:为每个线程提供独立的变量副本,避免线程间共享数据,从而从根本上解决资源竞争和数据一致性问题。
    • Python示例
import threading

local_data = threading.local()


def process_data():
    local_data.value = threading.current_thread().name
    print(f"Thread {local_data.value} has its own data: {local_data.value}")


threads = []
for i in range(3):
    t = threading.Thread(target = process_data)
    threads.append(t)
    t.start()

for t in threads:
    t.join()
  • Java示例
public class ThreadLocalExample {
    private static final ThreadLocal<Integer> threadLocal = ThreadLocal.withInitial(() -> 0);

    public static void main(String[] args) {
        Thread thread1 = new Thread(() -> {
            threadLocal.set(1);
            System.out.println("Thread 1: " + threadLocal.get());
        });
        Thread thread2 = new Thread(() -> {
            threadLocal.set(2);
            System.out.println("Thread 2: " + threadLocal.get());
        });
        thread1.start();
        thread2.start();
    }
}