MST

星途 面试题库

面试题:Python多线程结合数据处理中的资源竞争与锁机制

在Python中,使用多线程对共享数据(比如一个全局字典,存放不同用户的交易记录,格式为{user_id: [transaction1, transaction2, ...]})进行处理。其中一个线程负责不断向字典中添加新的交易记录,其他线程负责对每个用户的交易记录进行求和统计。请编写代码实现该功能,并详细说明如何处理线程间的资源竞争问题,以及解释不同锁机制(如互斥锁、信号量等)在该场景中的应用。
17.0万 热度难度
编程语言Python

知识考点

AI 面试

面试题答案

一键面试
import threading
import time

# 共享数据
user_transactions = {}
lock = threading.Lock()


# 添加交易记录的线程函数
def add_transaction(user_id, transaction):
    global user_transactions
    while True:
        with lock:
            if user_id not in user_transactions:
                user_transactions[user_id] = []
            user_transactions[user_id].append(transaction)
        time.sleep(1)


# 统计交易记录总和的线程函数
def sum_transactions(user_id):
    global user_transactions
    while True:
        with lock:
            if user_id in user_transactions:
                total = sum(user_transactions[user_id])
                print(f"User {user_id}'s total transaction: {total}")
        time.sleep(2)


# 创建并启动线程
add_thread = threading.Thread(target=add_transaction, args=(1, 10))
sum_thread = threading.Thread(target=sum_transactions, args=(1,))

add_thread.start()
sum_thread.start()

# 等待线程结束
add_thread.join()
sum_thread.join()

线程间资源竞争问题处理

  1. 使用锁机制:在上述代码中,我们使用了 threading.Lock() 创建了一个互斥锁 lock。在访问和修改共享数据 user_transactions 之前,使用 with lock: 语句获取锁,这能确保同一时间只有一个线程可以操作共享数据,从而避免资源竞争。

不同锁机制在该场景中的应用

  1. 互斥锁(Mutex)
    • 原理:互斥锁只有两种状态,锁定和未锁定。当一个线程获取了互斥锁,其他线程就无法获取,直到该线程释放锁。
    • 应用场景:在我们的例子中非常适用,因为共享数据 user_transactions 是一个全局字典,同一时间只允许一个线程进行读写操作,防止数据不一致。例如,如果两个线程同时尝试添加交易记录,可能导致数据覆盖或字典结构损坏。使用互斥锁就能避免这种情况。
  2. 信号量(Semaphore)
    • 原理:信号量维护了一个内部计数器,当线程获取信号量时,计数器减1,当线程释放信号量时,计数器加1。当计数器为0时,其他线程无法获取信号量。
    • 应用场景:如果我们希望控制同时访问共享数据的线程数量,可以使用信号量。例如,如果我们允许最多3个线程同时读取 user_transactions 字典进行求和统计,就可以创建一个初始值为3的信号量。这样既能提高并发效率,又能在一定程度上保证数据的一致性。不过在本题场景下,由于写操作需要严格的独占性,使用互斥锁更为直接和合适,信号量相对更适合读多写少且对并发读有一定控制需求的场景。