多线程用于IO、网络请求等地方,只要不是CPU密集型,都可以直接使用多线程。
线程锁在代码中发挥着关键的保护作用,让我详细解释它的工作原理:
锁的作用机制
1. 基本工作原理
with lock:if ensp_id in ensp_to_seq:return True
执行流程:
- 线程A进入
with lock:块,获取锁 - 线程A检查
ensp_id是否在字典中 - 在此期间,线程B尝试进入同一个
with lock:块,但被阻塞等待 - 线程A完成检查后退出
with块,自动释放锁 - 线程B现在可以获取锁并进入临界区
2. 为什么需要锁?
没有锁的情况(竞态条件):
# 线程A检查 ensp_id 不在字典中
if ensp_id not in ensp_to_seq: # 假设此时不在# 在线程A发起请求前,线程B也检查同一个ensp_id
# 线程B也发现不在字典中,也发起请求# 结果:同一个ensp_id被请求了两次!
有锁的情况:
with lock:if ensp_id in ensp_to_seq: # 线程A检查,获取锁return True# 线程A释放锁,发起请求...# 线程B必须等待线程A完成检查才能进入
3. 完整的线程安全流程
def get_ensembl_sequence(ensp_id: str):# 第一步:检查是否存在(需要锁保护)with lock:if ensp_id in ensp_to_seq: # 原子性操作return True# 第二步:网络请求(不需要锁,可以并行执行)headers = {"Content-Type": "text/x-fasta", "Accept": "text/x-fasta"}try:response = requests.get(url, headers=headers)response.raise_for_status()seq = response.text.strip()seq = "".join(seq.split("\n")[1:])# 第三步:写入结果(需要锁保护)with lock:ensp_to_seq[ensp_id] = seq # 原子性操作print(len(ensp_to_seq))return Trueexcept:return False
关键点:锁确保了检查-写入操作的原子性,避免了竞态条件,保证了数据的一致性。