【Python】基本语法:线程、锁

1、线程:threading.Thread

1)线程 Thread 在模块 threading 中,导入模块

python 复制代码
import threading

2)Thread 构造函数的参数

python 复制代码
    def __init__(self, group=None, target=None, name=None,
                 args=(), kwargs=None, *, daemon=None):
  • group 必须为 None;预留参数,用于线程组,目前未实现。
  • target:是由 run() 方法调用的可执行对象(函数)。
  • name:线程名称。默认会自动生成唯一名称,格式为 "Thread-N"。
  • args:调用 target 时的参数(列表/元组),默认空元组 ();
    多个参数的写法(a,b,c,),单个参数务必写逗号 (self.server,)
  • kwargs:调用 target 时用的关键字参数字典,默认空字典 {}。
  • daemon:是否为守护线程:True=主线程退出时自动结束此线程,否则主线程阻塞,等待该线程退出

3)用法一:函数传递给target

python 复制代码
import threading

def work(name):
    print(f"线程 {name} ")

t1 = threading.Thread(target=work, args=("A",))
t1.start()
t1.join()
  • start():启动线程,开始运行传递给target的函数
  • join():主线程等待该线程执行完毕再继续往下走

4)用法二:继承Thread,重载run函数

python 复制代码
import threading

class MyThread(threading.Thread):
    def run(self):
        print("自定义线程")

t = MyThread()
t.start()
t.join()

2、线程同步

2.1 Lock 互斥锁

1)一般写法

python 复制代码
lock = threading.Lock()
lock.acquire()  加锁
......临界区代码......
lock.release()  释放锁

2)自动释放锁写法

python 复制代码
with lock:
    ......临界区代码......
    pass

pass 表示空语句,用来满足python缩进代码块

2.2 RLock 可重入锁

避免同一线程重复加锁导致死锁

python 复制代码
def func_a():
    with rlock:
        func_b()  # 嵌套再次加锁,RLock 支持

def func_b():
    with rlock:
        print("临界区执行")

2.3 Semaphore 信号量

先创建一个计数器,并设置号初始值;

  • P 操作 (acquire)一次,申请一个资源,计数器 -1;如果计数器的值为0,不能再-1,阻塞在这里,直到有资源释放,计数器变为正值;
  • V 操作 (release)一次,释放一个资源,计数器 +1;如果计数器的值大于0,会唤醒阻塞的线程
  • 多次V 操作,超过初始值会怎么样?不会报错,但是逻辑上会超出设计意图,建议使用 BoundedSemaphore 有界信号量,超过初始值会报异常 ValueError 来提示
python 复制代码
import threading
import time

empty = threading.Semaphore(5)  # 空槽位数量
full = threading.Semaphore(0)   # 已生产产品数量
buffer = []

def producer():
    for i in range(10):
        empty.acquire()
        buffer.append(i)
        print(f"生产: {i}, buffer={buffer}")
        full.release()
        time.sleep(0.2)

def consumer():
    for i in range(10):
        full.acquire()
        item = buffer.pop(0)
        print(f"消费: {item}, buffer={buffer}")
        empty.release()
        time.sleep(0.5)

t1 = threading.Thread(target=producer)
t2 = threading.Thread(target=consumer)
t1.start()
t2.start()
t1.join()
t2.join()

2.4 Condition 条件变量

Condition 内部 = 1 把锁(默认 RLock) + 1 个等待线程队列(_waiters = _deque())

  • 使用 Condition 创建的对象是共享对象,生产者线程、消费者线程都引用同一个实例
  • 所有 cond.wait()的线程都会进入同一个等待队列(_waiters )
  • notify() / notify_all() 就是从这个队列里唤醒线程(_waiters )

1)生产者等待条件:缓冲区满

python 复制代码
while len(buffer) >= MAX_SIZE:
    cond.wait()

2)消费者等待条件:缓冲区空

python 复制代码
while len(buffer) == 0:
    cond.wait()

3)当 notify_all() 唤醒所有等待线程:

  • 生产者线程醒来:检查 while 条件,如果缓冲区仍然满,再次 wait 回去
  • 消费者线程醒来:检查 while 条件,如果缓冲区有数据,正常消费

4)全程只使用一个 Condition 即可

生产者、消费者的完整示例

python 复制代码
import threading
import time

# 创建条件变量
cond = threading.Condition()
buffer = []
MAX_SIZE = 3  # 缓冲区最大容量

# 生产者
def producer():
    for i in range(10):
        with cond:
            # 缓冲区满则等待
            while len(buffer) >= MAX_SIZE:
                print(f"缓冲区已满,生产者等待...")
                cond.wait()

            # 放入产品
            buffer.append(i)
            print(f"生产 {i}, buffer={buffer}")
            cond.notify_all()  # 通知消费者
        time.sleep(0.3)

# 消费者
def consumer():
    for _ in range(10):
        with cond:
            # 缓冲区空则等待
            while len(buffer) == 0:
                print(f"缓冲区为空,消费者等待...")
                cond.wait()

            # 取出产品
            item = buffer.pop(0)
            print(f"消费 {item}, buffer={buffer}")
            cond.notify_all()  # 通知生产者
        time.sleep(0.5)

t_prod = threading.Thread(target=producer)
t_cons = threading.Thread(target=consumer)
t_prod.start()
t_cons.start()
t_prod.join()
t_cons.join()

2.5 Event 事件(全局信号)

用途:主线程统一发启动信号,批量启动工作线程

  • 一行 wait()等待全局信号,适合启停控制
  • 一次性广播通知 set() ,批量唤醒所有等待线程,代码干净
python 复制代码
import threading
import time

def worker(e, idx):
    print(f"线程{idx}:等待全局启动信号")
    e.wait()  # 阻塞直到 set()
    print(f"线程{idx}:收到信号,开始执行任务")
    time.sleep(1)
    print(f"线程{idx}:任务结束")

if __name__ == "__main__":
    start_event = threading.Event()
    threads = [threading.Thread(target=worker, args=(start_event, i)) for i in range(3)]
    for t in threads:
        t.start()

    time.sleep(2)
    print("主线程:发送全局启动信号")
    start_event.set()  # 全部线程同时放行

    for t in threads:
        t.join()

2.6 Barrier 屏障

用途:原生实现 N 线程卡点同步:保证全部 N 线程就绪后,才一起继续执行,解决多线程阶段同步;

并行仿真、数值计算:所有线程完成数据加载、初始化,再同步开始主计算

示例

python 复制代码
import threading
import time

barrier = threading.Barrier(parties=3, action=lambda: print("=== 全部线程就绪,同步开始计算 ==="))

def worker(idx):
    print(f"线程{idx}:正在加载初始化数据...")
    time.sleep(idx * 0.5)  # 每个线程初始化耗时不同
    print(f"线程{idx}:到达屏障点,等待其余线程")
    try:
        barrier.wait(timeout=5)  # 卡点等待3个线程集齐
    except threading.BrokenBarrierError:
        print(f"线程{idx}:屏障异常中断")
        return
    print(f"线程{idx}:同步执行正式计算")
    time.sleep(1)

if __name__ == "__main__":
    threads = [threading.Thread(target=worker, args=(i,)) for i in range(3)]
    for t in threads:
        t.start()
    for t in threads:
        t.join()
    print("本轮计算完成")

输出

bash 复制代码
线程0:正在加载初始化数据...
线程0:到达屏障点,等待其余线程
线程1:正在加载初始化数据...
线程2:正在加载初始化数据...
线程1:到达屏障点,等待其余线程
线程2:到达屏障点,等待其余线程
=== 全部线程就绪,同步开始计算 ===
线程0:同步执行正式计算
线程2:同步执行正式计算
线程1:同步执行正式计算
本轮计算完成
相关推荐
小白学大数据1 小时前
企业内网场景下 Python 自定义 CA 证书信任链的正确配置方法
开发语言·网络·python·信任链
Java面试题总结1 小时前
使用 Python 在 Excel 中添加和自定义文本框
开发语言·python·excel
m沐沐2 小时前
【机器学习】基于 dlib 面部关键点的多表情分类
人工智能·python·深度学习·机器学习·计算机视觉·人脸识别·表情识别
️学习的小王2 小时前
Python + MySQL 学生信息管理系统实战教程
android·python·mysql
lbb 小魔仙2 小时前
从零搭建Python开发环境:Python安装 + VSCode + PyCharm 完整配置指南
vscode·python·pycharm
ch_atu2 小时前
使用python下载modelscope上的模型
开发语言·python
spssau3 小时前
一文学会结构方程模型:从模型搭建、路径图绘制到不达标调整,实操全流程
人工智能·python·算法
Albart5753 小时前
Python爬虫请求头伪装后仍被反爬,基于代理池+随机延迟的绕过实战
开发语言·爬虫·python
只会安静敲代码的 小周3 小时前
AI应用开发
python