MST

星途 面试题库

面试题:Python多线程与异步编程结合之资源管理

在Python中结合多线程与异步编程时,可能会遇到资源竞争的问题。比如,多个线程和异步任务都要访问和修改同一个共享数据。请阐述你会采用哪些方法来避免这种资源竞争,并且以一个银行转账的场景为例(涉及到对账户余额这一共享数据的修改),用代码展示如何有效管理资源,防止数据不一致。
41.9万 热度难度
编程语言Python

知识考点

AI 面试

面试题答案

一键面试

避免资源竞争的方法

  1. 锁机制
    • 互斥锁(Mutex):线程或异步任务在访问共享资源前获取锁,访问完后释放锁,同一时间只有一个线程或任务能持有锁,从而避免竞争。
    • 信号量(Semaphore):与互斥锁类似,但可以允许多个线程或任务同时访问共享资源,通过设置信号量的初始值来控制同时访问的数量。
  2. 队列:使用队列来传递数据,线程或异步任务将数据放入队列,其他线程或任务从队列中取出数据进行处理,避免直接访问共享数据。
  3. 线程本地存储(TLS):每个线程有自己独立的存储空间,避免不同线程之间的数据竞争。在异步编程中,可以使用类似的上下文管理机制。

银行转账场景代码示例

import threading
import asyncio

# 定义账户余额
account_balance = 1000
# 创建互斥锁
lock = threading.Lock()


# 线程函数实现转账
def transfer_thread(amount):
    global account_balance
    with lock:
        if account_balance >= amount:
            account_balance -= amount
            print(f"Thread transferred {amount}, new balance: {account_balance}")
        else:
            print("Insufficient balance for thread transfer")


# 异步函数实现转账
async def transfer_async(amount):
    global account_balance
    async with asyncio.Lock():
        if account_balance >= amount:
            account_balance -= amount
            print(f"Async transferred {amount}, new balance: {account_balance}")
        else:
            print("Insufficient balance for async transfer")


# 创建线程
threads = []
for _ in range(3):
    t = threading.Thread(target=transfer_thread, args=(200,))
    threads.append(t)
    t.start()

# 等待所有线程完成
for t in threads:
    t.join()

# 创建异步任务
async def main():
    tasks = []
    for _ in range(3):
        task = asyncio.create_task(transfer_async(200))
        tasks.append(task)
    await asyncio.gather(*tasks)


asyncio.run(main())

在上述代码中,对于线程操作,使用 threading.Lock 来确保在修改账户余额时不会出现资源竞争。对于异步操作,使用 asyncio.Lock 达到相同的目的。通过这种方式,无论是多线程还是异步任务,在进行银行转账操作时都能有效管理资源,防止数据不一致。