MST

星途 面试题库

面试题:Python空字典在多线程编程中的挑战与解决方案

在多线程Python程序中,多个线程可能会同时访问和修改一个字典。如果初始时字典为空,描述在这种场景下可能会遇到的问题,例如数据竞争、死锁等,并给出至少两种有效的解决方案,同时用代码示例展示如何正确地在多线程环境中使用空字典。
44.4万 热度难度
编程语言Python

知识考点

AI 面试

面试题答案

一键面试

可能遇到的问题

  1. 数据竞争:多个线程同时对字典进行读写操作时,可能会导致数据不一致。例如,一个线程正在读取字典中的某个值,而另一个线程同时修改了这个值,这会导致读取线程得到一个不确定的值。
  2. 死锁:如果线程在访问字典时需要获取多个锁,并且获取锁的顺序不一致,就可能会产生死锁。例如,线程A获取锁1,然后尝试获取锁2;而线程B获取锁2,然后尝试获取锁1,此时两个线程相互等待对方释放锁,就会陷入死锁。

解决方案

  1. 使用threading.Lock:通过锁来控制对字典的访问,确保同一时间只有一个线程可以访问和修改字典。
import threading

my_dict = {}
lock = threading.Lock()

def update_dict(key, value):
    with lock:
        my_dict[key] = value

def read_dict(key):
    with lock:
        return my_dict.get(key)

thread1 = threading.Thread(target=update_dict, args=('key1', 'value1'))
thread2 = threading.Thread(target=read_dict, args=('key1',))

thread1.start()
thread2.start()

thread1.join()
thread2.join()
  1. 使用collections.deque的线程安全队列和Queue.Queue:将对字典的操作放入队列中,由一个专门的线程来处理队列中的任务,这样可以避免多个线程直接访问字典。
import threading
from queue import Queue

my_dict = {}
task_queue = Queue()

def worker():
    while True:
        task, args = task_queue.get()
        if task == 'update':
            key, value = args
            my_dict[key] = value
        elif task =='read':
            key = args[0]
            print(my_dict.get(key))
        task_queue.task_done()

worker_thread = threading.Thread(target=worker)
worker_thread.daemon = True
worker_thread.start()

def update_dict(key, value):
    task_queue.put(('update', (key, value)))

def read_dict(key):
    task_queue.put(('read', (key,)))

thread1 = threading.Thread(target=update_dict, args=('key1', 'value1'))
thread2 = threading.Thread(target=read_dict, args=('key1',))

thread1.start()
thread2.start()

thread1.join()
thread2.join()

task_queue.join()
  1. 使用threading.RLockRLock(可重入锁)允许同一个线程多次获取锁,而不会造成死锁。在需要递归调用或者一个线程内多次获取锁的场景下比较有用。
import threading

my_dict = {}
rlock = threading.RLock()

def recursive_update(key, value):
    with rlock:
        if key in my_dict:
            sub_dict = my_dict[key]
            recursive_update('sub_key','sub_value', sub_dict)
        else:
            my_dict[key] = value

def recursive_update(key, value, sub_dict=None):
    if sub_dict is None:
        sub_dict = my_dict
    with rlock:
        if key in sub_dict:
            sub_sub_dict = sub_dict[key]
            recursive_update('sub_key','sub_value', sub_sub_dict)
        else:
            sub_dict[key] = value


thread = threading.Thread(target=recursive_update, args=('outer_key', 'outer_value'))
thread.start()
thread.join()