MST

星途 面试题库

面试题:Python多线程环境下锁与同步机制对性能的影响

在Python多线程编程中,使用锁与同步机制虽然能保证数据一致性,但也可能对性能产生影响。请阐述锁与同步机制会在哪些方面影响性能,并举例说明如何优化以减少这种影响。
13.6万 热度难度
编程语言Python

知识考点

AI 面试

面试题答案

一键面试

锁与同步机制影响性能的方面

  1. 竞争开销:多个线程竞争同一把锁时,会产生上下文切换开销。操作系统需要暂停当前持有锁的线程,调度等待锁的线程,这涉及到保存和恢复线程的运行状态,消耗CPU时间。例如在一个高并发的Web服务器应用中,若多个线程频繁竞争数据库连接锁,大量时间将浪费在上下文切换上。
  2. 死锁风险:如果线程获取锁的顺序不当,可能导致死锁。此时线程相互等待对方释放锁,都无法继续执行,整个程序陷入停滞,这不仅影响当前性能,甚至导致程序崩溃。例如,线程A获取锁1后等待锁2,线程B获取锁2后等待锁1,就形成死锁。
  3. 锁粒度问题:锁的粒度若设置过大,会导致过多资源被锁定,其他线程长时间等待。比如,一个函数对整个数据结构加锁,即使只需要修改其中一小部分数据,其他线程对该数据结构任何部分的访问都要等待锁释放,降低了并发度。
  4. 饥饿现象:高优先级线程频繁获取锁,低优先级线程可能长时间无法获取锁,从而得不到执行机会,影响整体系统的公平性和性能。例如在一个多媒体处理程序中,负责实时视频流处理的高优先级线程持续抢占锁,导致负责音频处理的低优先级线程饥饿。

优化方式及示例

  1. 减小锁粒度
    • 优化思路:将大锁分解为多个小锁,只在访问需要同步的最小数据单元时加锁。
    • 示例代码
import threading

class SmallLockOptimization:
    def __init__(self):
        self.data1 = 0
        self.data2 = 0
        self.lock1 = threading.Lock()
        self.lock2 = threading.Lock()

    def update_data1(self):
        with self.lock1:
            self.data1 += 1

    def update_data2(self):
        with self.lock2:
            self.data2 += 1
  1. 合理安排锁获取顺序
    • 优化思路:所有线程按照相同顺序获取锁,避免死锁。
    • 示例代码
import threading

lock1 = threading.Lock()
lock2 = threading.Lock()

def thread_function1():
    with lock1:
        with lock2:
            # 执行需要同步的操作
            pass

def thread_function2():
    with lock1:
        with lock2:
            # 执行需要同步的操作
            pass
  1. 使用读写锁
    • 优化思路:对于读多写少的场景,使用读写锁。读操作可以同时进行,写操作需要独占锁。
    • 示例代码
import threading

class ReadWriteLockOptimization:
    def __init__(self):
        self.data = 0
        self.read_lock = threading.Lock()
        self.write_lock = threading.Lock()
        self.read_count = 0

    def read_data(self):
        with self.read_lock:
            self.read_count += 1
            if self.read_count == 1:
                self.write_lock.acquire()
        # 执行读操作
        with self.read_lock:
            self.read_count -= 1
            if self.read_count == 0:
                self.write_lock.release()

    def write_data(self):
        with self.write_lock:
            # 执行写操作
            self.data += 1
  1. 使用信号量替代锁
    • 优化思路:信号量可以控制同时访问资源的线程数量,在一些场景下比锁更灵活。
    • 示例代码
import threading

semaphore = threading.Semaphore(5)  # 允许5个线程同时访问

def semaphore_usage():
    with semaphore:
        # 执行操作
        pass