设为首页收藏本站
网站公告 | 这是第一条公告
     

 找回密码
 立即注册
缓存时间09 现在时间09 缓存数据 随缘不是听天由命,而是用豁达的心态去面对生活。心累的时候,不如换个角度看世界。新的一天,早安。

随缘不是听天由命,而是用豁达的心态去面对生活。心累的时候,不如换个角度看世界。新的一天,早安。

查看: 1128|回复: 1

Python多线程与同步机制浅析

[复制链接]

  离线 

TA的专栏

  • 打卡等级:热心大叔
  • 打卡总天数:173
  • 打卡月天数:0
  • 打卡总奖励:3070
  • 最近打卡:2023-08-27 10:20:00
等级头衔

等級:晓枫资讯-信息监察员

在线时间
5 小时

积分成就
威望
0
贡献
21
主题
55
精华
0
金钱
3241
积分
100
注册时间
2022-12-23
最后登录
2023-8-27

发表于 2022-12-25 11:18:51 | 显示全部楼层 |阅读模式
线程实现

Python中线程有两种方式:函数或者用类来包装线程对象。threading模块中包含了丰富的多线程支持功能:

  • threading.currentThread(): 返回当前线程;
  • threading.enumerate(): 返回包含正在运行的线程列表;
  • threading.activeCount(): 返回正在运行的线程数量,与len(threading.enumerate())等价。

Thread类

通过Thread类来处理线程,类中提供的一些方法:

  • run(): 用以表示线程执行的方法(可重载实现实际功能);
  • start(): 启动线程;
  • join([time]): 等待线程中止(或者超时);
  • isAlive(): 返回线程是否活动;
  • getName(): 返回线程名;
  • setName(): 设置线程名;
  • setDaemon(True):设置为后台进程(必须在start调用前设定)。

函数方式

通过Thread直接构造线程,然后通过start方法启动线程:

threading.Thread(group=None, target=None, name=None, args=(), kwargs=None, *,daemon=None)

各参数说明:

  • group:指定线程隶属的线程组(当前忽略);
  • target:指定线程要调度的目标方法(即实现功能的函数);
  • args:传递给目标方法的参数(以元组的方式);
  • kwargs:传递给目标方法的参数(以字典的方式);
  • daemon:指定线程是否为后台线程。

  1. def simpleRoutine(name, delay):
  2.     print(f"routine {name} starting...")
  3.     time.sleep(delay)
  4.     print(f"routine {name} finished")
  5. if __name__ == '__main__':
  6.     thrOne = threading.Thread(target=simpleRoutine, args=("First", 1))
  7.     thrTwo = threading.Thread(target=simpleRoutine, args=("Two", 2))
  8.     thrOne.start()
  9.     thrTwo.start()
  10.     thrOne.join()
  11.     thrTwo.join()
复制代码
继承方式

直接继承Thread,创建一个新的子类(主要实现run方法):

  1. class SimpleThread (threading.Thread):
  2.     def __init__(self, name, delay):
  3.         # threading.Thread.__init__(self)
  4.         super().__init__()
  5.         self.name = name
  6.         self.delay = delay
  7.     def run(self):
  8.         print(f"thread {self.name} starting...")
  9.         time.sleep(self.delay)
  10.         print(f"thread {self.name} finished")
  11. if __name__ == '__main__':
  12.     thrOne = SimpleThread("First", 2)
  13.     thrTwo = SimpleThread("Second", 2)
  14.     thrOne.start()
  15.     thrTwo.start()
  16.     thrOne.join()
  17.     thrTwo.join()
复制代码
同步机制

当多个线程同时修改同一条数据时可能会出现脏数据;所以,就需要线程锁,即同一时刻只允许一个线程执行操作。


同步锁Lock

threading提供了Lock和RLock(可重入锁)两个类,它们都提供了如下两个方法来加锁和释放锁:

  • acquire(blocking=True, timeout=-1):加锁,其中 timeout 参数指定加锁多少秒。
  • release():释放锁。

两种使用锁的方式:

  1. gCount = 0
  2. def PlusOne(locker):
  3.     global gCount
  4.       with locker:
  5.           gCount += 1、
  6. def MinusOne(locker):
  7.     global gCount
  8.       if locker.acquire():
  9.           gCount -= 1
  10.           locker.release()
复制代码
条件变量Condition

Condition对象内部维护了一个锁(构造时可传递一个Lock/RLock对象,否则内部会自行创建一个RLock)和一个waiting池:

  • 通过acquire获得Condition对象;
  • 当调用wait方法时,线程会释放Condition内部的锁并进入blocked状态,同时在waiting池中记录这个线程;
  • 当调用notify方法时,Condition对象会从waiting池中挑选一个线程,通知其调用acquire方法尝试取到锁。

Condition对象:

__init__(self,lock=None):Condition类总是与一个锁相关联(若不指定lock参数,会自动创建一个与之绑定的RLock对象);

acquire(timeout):调用关联锁的acquire()方法;

release():调用关联锁的release()方法

wait(timeout):线程挂起,直到收到一个notify通知或超时才会被唤醒;必须在已获得锁的前提下调用;

notify(n=1):唤醒waiting池中的n个正在等待的线程并通知它:

  • 收到通知的线程将自动调用acquire()方法尝试加锁;
  • 若waiting池中有多个线程,随机选择n个唤醒;
  • 必须在已获得锁的前提下调用,否则将引发错误。

notify_all():通知所有线程。

  1. class Producer(threading.Thread):
  2.     def __init__(self, cond, storage):
  3.         threading.Thread.__init__(self)
  4.         self.cond = cond
  5.         self.storage = storage
  6.     def run(self):
  7.         label = 1
  8.         while True:
  9.             with self.cond:
  10.                 if len(self.storage) < 10:
  11.                     self.storage.append(label)
  12.                     print(f"<- Produce {label} product")
  13.                     label += 1
  14.                     self.cond.notify(2)
  15.                 else:
  16.                     print(f"<- storage full: Has Produced {label - 1} product")
  17.                     self.cond.notify_all()
  18.                     self.cond.wait()
  19.                 time.sleep(0.4)
  20. class Consumer(threading.Thread):
  21.     def __init__(self, name, cond, storage):
  22.         threading.Thread.__init__(self)
  23.         self.name = name
  24.         self.cond = cond
  25.         self.storage = storage
  26.     def run(self):
  27.         while True:
  28.             if self.cond.acquire():
  29.                 if len(self.storage) > 1:
  30.                     pro = self.storage.pop(0)
  31.                     print(f"-> {self.name} consumed {pro}")
  32.                     self.cond.notify()
  33.                 else:
  34.                     print(f"-> {self.name} storage empty: no product to consume")
  35.                     self.cond.wait()
  36.                 self.cond.release()
  37.                 time.sleep(1)
复制代码
信号量Semaphore

信号量对象内部维护一个计数器:

  • acquire(blocking=True,timeout=None)时减1,当计数为0就阻塞请求的线程;
  • release()时加1,当计数大于0恢复被阻塞的线程;

threading中有Semaphore和BoundedSemaphore两个信号量;BoundedSemaphore限制了release的次数,任何时候计数器的值,都不不能大于初始值(release时会检测计数器的值,若大于等于初始值,则抛出ValueError异常)。

通过Semaphore维护生产(release一个)、消费(acquire一个)量:

  1. # products = threading.Semaphore(0)
  2. def produceOne(label, sem: threading.Semaphore):
  3.     sem.release()
  4.     print(f"{label} produce one")
  5. def consumeOne(label, sem: threading.Semaphore):
  6.     sem.acquire()
  7.     print(f"{label} consume one")
复制代码

通过BoundedSemaphore来控制并发数量(最多有Semaphore初始值数量的线程并发):

  1. # runner = threading.BoundedSemaphore(3)
  2. def runBound(name, sem: threading.BoundedSemaphore):
  3.     with sem:
  4.         print(f"{name} is running")
  5.         time.sleep(1)
  6.         print(f"{name} finished")
复制代码
事件Event

事件对象内部有个标志字段,用于线程等待事件的发生:

  • isSet():返回event的状态值;
  • wait():状态为False时,一直阻塞;否则立即返回;
  • set(): 设置状态值为True,激活所有被阻塞的线程;
  • clear():恢复状态值为False。

多线程等待事件发生,然后开始执行:

  1. def waiters(name, evt: threading.Event):
  2.     evt.wait()
  3.     print(f"{name} is running")
  4.     time.sleep(1)
  5.     print(f"{name} finished")
  6. def starting(evt: threading.Event):
  7.     evt.set()
  8.     print("event is set")
复制代码
屏障Barrier

屏障用于设定等待线程数量,当数量达到指定值时,开始执行:

threading.Barrier(parties, action=None, timeout=None)

屏障属性与方法:

  • wait(timeout=None):等待通过屏障;线程被阻塞,直到阻塞的数量达到parties时,被阻塞的线程被同时全部释放;
  • reset():重置屏障到默认的空状态;
  • abort():将障碍置为断开状态;导致等待的线程引发BrokenBarrierError异常;
  • partier():通过障碍所需的线程数;
  • n_waiting():当前在屏障中等待的线程数;
  • broken():如果屏障处于断开状态,则返回True。

  1. def waitBarrier(name, barr: threading.Barrier):
  2.     print(f"{name} waiting for open")
  3.     try:
  4.         barr.wait()
  5.         print(f"{name} running")
  6.         time.sleep(5)
  7.     except threading.BrokenBarrierError:
  8.         print(f"{name} exception")
  9.     print(f"{name} finished")
复制代码
GIL全局解释器锁

GIL(Global Interpreter Lock,全局解释器锁);cpython中,某个线程想要执行,必须先拿到GIL(可以把GIL看作是“通行证”)。每次释放GIL锁,线程都要进行锁竞争,切换线程,会消耗资源。

由于GIL锁的存在,python里一个进程永远只能同时执行一个线程(拿到GIL的线程),这就是为什么在多核CPU上,python的多线程效率并不高:

  • CPU密集型代码:由于计算工作多,会很快用完时间片,然后触发GIL的释放与再竞争;
  • IO密集型代码(文件处理、网络爬虫等):多线程能够有效提升效率(单线程下有IO操作会进行IO等待,造成不必要的时间浪费,而开启多线程能在线程A等待时,自动切换到线程B,可以不浪费CPU的资源,从而能提升程序执行效率)。

python在使用多线程的时候,调用的是c语言的原生线程:

  • 拿到公共数据
  • 申请GIL
  • python解释器调用os原生线程
  • os操作cpu执行运算
  • 当线程执行时间到后,就进行切换(context switch)

到此这篇关于Python多线程与同步机制浅析的文章就介绍到这了,




晓枫资讯-科技资讯社区-免责声明
免责声明:以上内容为本网站转自其它媒体,相关信息仅为传递更多信息之目的,不代表本网观点,亦不代表本网站赞同其观点或证实其内容的真实性。
      1、注册用户在本社区发表、转载的任何作品仅代表其个人观点,不代表本社区认同其观点。
      2、管理员及版主有权在不事先通知或不经作者准许的情况下删除其在本社区所发表的文章。
      3、本社区的文章部分内容可能来源于网络,仅供大家学习与参考,如有侵权,举报反馈:点击这里给我发消息进行删除处理。
      4、本社区一切资源不代表本站立场,并不代表本站赞同其观点和对其真实性负责。
      5、以上声明内容的最终解释权归《晓枫资讯-科技资讯社区》所有。
http://bbs.yzwlo.com 晓枫资讯--游戏IT新闻资讯~~~

  离线 

TA的专栏

等级头衔

等級:晓枫资讯-列兵

在线时间
0 小时

积分成就
威望
0
贡献
0
主题
0
精华
0
金钱
17
积分
14
注册时间
2022-12-29
最后登录
2022-12-29

发表于 2023-1-24 02:19:40 | 显示全部楼层
谢谢分享~~~~~一定要观光下~~~~~
http://bbs.yzwlo.com 晓枫资讯--游戏IT新闻资讯~~~
严禁发布广告,淫秽、色情、赌博、暴力、凶杀、恐怖、间谍及其他违反国家法律法规的内容。!晓枫资讯-社区
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

1楼
2楼

手机版|晓枫资讯--科技资讯社区 本站已运行

CopyRight © 2022-2025 晓枫资讯--科技资讯社区 ( BBS.yzwlo.com ) . All Rights Reserved .

晓枫资讯--科技资讯社区

本站内容由用户自主分享和转载自互联网,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责。

如有侵权、违反国家法律政策行为,请联系我们,我们会第一时间及时清除和处理! 举报反馈邮箱:点击这里给我发消息

Powered by Discuz! X3.5

快速回复 返回顶部 返回列表