面试题答案
一键面试多线程中处理资源竞争
在多线程编程中,通过锁机制(如threading.Lock
)来处理资源竞争。
- 原理:锁本质上是一个标志,当一个线程获取到锁时,其他线程就无法获取,直到该线程释放锁。这样可以保证在同一时间只有一个线程能够访问共享资源,避免资源竞争。
- 实现示例:
import threading
# 共享资源
counter = 0
lock = threading.Lock()
def increment():
global counter
for _ in range(1000000):
lock.acquire()
try:
counter += 1
finally:
lock.release()
thread1 = threading.Thread(target=increment)
thread2 = threading.Thread(target=increment)
thread1.start()
thread2.start()
thread1.join()
thread2.join()
print(counter)
在上述代码中,lock.acquire()
获取锁,lock.release()
释放锁,try - finally
语句确保无论在counter += 1
过程中是否发生异常,锁都会被释放。
异步编程中处理资源竞争
在异步编程(如使用asyncio
库)中,通过asyncio.Lock
来处理共享资源的访问冲突。
- 原理:与多线程锁类似,
asyncio.Lock
也是一个标志,当一个协程获取到锁时,其他协程必须等待锁的释放才能获取锁并访问共享资源。但异步编程是基于事件循环的单线程执行模型,协程的切换是在代码执行到await
语句时主动让出控制权,而不是像多线程那样由操作系统调度。 - 实现示例:
import asyncio
# 共享资源
counter = 0
lock = asyncio.Lock()
async def increment():
global counter
for _ in range(1000000):
await lock.acquire()
try:
counter += 1
finally:
lock.release()
async def main():
task1 = asyncio.create_task(increment())
task2 = asyncio.create_task(increment())
await task1
await task2
if __name__ == "__main__":
asyncio.run(main())
print(counter)
在上述代码中,await lock.acquire()
获取锁,lock.release()
释放锁,try - finally
语句确保无论在counter += 1
过程中是否发生异常,锁都会被释放。
异同点分析
- 相同点
- 目的相同:两者都是为了处理共享资源的竞争问题,确保同一时间只有一个执行单元(线程或协程)能够访问共享资源,防止数据不一致等问题。
- 实现方式类似:都通过锁机制来实现,获取锁才能访问共享资源,访问完毕后释放锁。都使用
try - finally
结构来确保锁在使用完毕后被正确释放,即使在访问共享资源过程中发生异常。
- 不同点
- 原理不同:多线程基于操作系统的线程调度,多个线程可以并行执行(多核CPU),资源竞争是由于多个线程同时访问共享资源。而异步编程基于单线程的事件循环,协程通过
await
语句主动让出执行权,资源竞争是由于异步I/O操作等导致协程切换时对共享资源的访问冲突。 - 性能和适用场景不同:多线程适用于CPU密集型任务和需要利用多核CPU的场景,但线程创建和切换开销较大。而异步编程适用于I/O密集型任务,通过减少线程切换开销提高性能,在单线程内高效处理大量I/O操作。
- 原理不同:多线程基于操作系统的线程调度,多个线程可以并行执行(多核CPU),资源竞争是由于多个线程同时访问共享资源。而异步编程基于单线程的事件循环,协程通过