一、Thread类简介

1、Thread类参数简介

class threading.Thread(group=None, target=None, name=None, args=(), kwargs={}, *, daemon=None)
group:目前此参数为None,在实现ThreadGroup类时为将来的扩展保留。
target:target接收的是一个函数的地址,由run()方法调用执行函数中的内容。默认为无,表示未调用任何内容。
name :线程名,可自行定义。
args:target接收的是函数名,此函数的位置参数以元组的形式存放在args中,用于执行函数时调用。
kwargs :target接收的是函数名,此函数的关键字参数以字典的形式存放在kwargs中,用于执行函数时调用。
daemon:如果为True表示该线程为守护线程。

2、Thread类的常用方法

start():开启线程,一个Thread对象只能调用一次start()方法,如果在同一线程对象上多次调用此方法,则会引发RuntimeError。

run():执行start()方法会调用run(),该方将创建Thread对象时传递给target的函数名,和传递给args、kwargs的参数组合成一个完整的函数,并执行该函数。run()方法一般在自定义Thead类时会用到。

join(timeout=None):join会阻塞、等待线程,timeout单位为秒,因为join()总是返回none,所以在设置timeout调用join(timeout)之后,需要使用isalive()判断线程是否执行完成,如果isalive为True表示线程在规定时间内没有执行完,线程超时。如果join(timeout=None)则会等待线程执行完毕后才会执行join()后面的代码,一般用于等待线程结束。

name:获取线程名。

getName():获取线程名。

setName(name):设置线程名。

ident:“线程标识符”,如果线程尚未启动,则为None。如果线程启动是一个非零整数。

is_alive():判断线程的存活状态,在run()方法开始之前,直到run()方法终止之后。如果线程存活返回True,否则返回False。

daemon:如果thread.daemon=True表示该线程为守护线程,必须在调用Start()之前设置此项,否则将引发RuntimeError。默认为False

isDaemon():判断一个线程是否是守护线程。

setDaemon(daemonic):设置线程为守护线程。


二、Thread的简单使用

1、创建线程:

from threading import Threaddef work(args,kwargs=None):print(args)print(kwargs)if __name__ == "__main__":t = Thread(target=work, args=(("我是位置参数"),), kwargs={'kwargs': '我是关键字参数'}, name='我是线程demo')print(t.name)  # 打印线程名t.start()          # 开启线程print('我是主线程')# 打印内容如下
我是线程demo
我是位置参数
我是关键字参数
我是主线程

由上面的打印内容我们可以看出,在执行完所有线程后才执行的主线程print。如果是多进程的话会先执行主进程中的print然后才会执行子进程的print。主要是因为开启进程相比于开启线程更加耗费时间。

2、自定义线程类:

from threading import Threaddef work(args,kwargs=None):print(args)print(kwargs)class MyThread(Thread):  # 使用继承Thread的方式,自定义线程类def __init__(self,target=None, name=None,args=(), kwargs=None, *, daemon=None):# 如果要给对象封装属性,必须先调用父类super().__init__()if kwargs is None:kwargs = {}self._target = targetself._name = nameself._args = argsself._kwargs = kwargsdef run(self):  # 必须要有run类,因为start要调用print(f"我重写了Thread类的run")self._target(*self._args,**self._kwargs)if __name__ == "__main__":t = MyThread(target=work,args=(('我是位置参数'),),kwargs={'kwargs':'我是关键字参数'},name='我是自定义线程类') # 创建线程对象print(t.name)  # 打印线程名t.start()      # 开启线程print("主线程")# 打印内容如下
我是自定义线程类
我重写了Thread类的run
主线程
我是位置参数
我是关键字参数

3、多进程和多线程的效率对比:

from threading import Thread
from multiprocessing import Process
import timedef thread_work(name):print(f"{name}")
def process_work(name):print(f"{name}")if __name__ == "__main__":# 进程执行效率pro = []start = time.time()for i in range(3):p = Process(target=process_work,args=(("进程-"+str(i)),))p.start()pro.append(p)for i in pro:i.join()end = time.time()print("进程运行了:%s" %(end - start))# 线程执行效率thread_l = []start = time.time()for i in range(3):t = Thread(target=process_work, args=(("线程-" + str(i)),))t.start()thread_l.append(t)for i in thread_l:i.join()end = time.time()print("进程运行了:%s" % (end - start))# 打印内容如下
进程-0
进程-1
进程-2
进程运行了:0.18501067161560059
线程-0
线程-1
线程-2
进程运行了:0.004000186920166016

我们可以从时间上看出,线程的效率是远远高于进程的。

4、守护线程

主线程会等待所有非守护线程执行完毕后,才结束主线程。主进程是进程内的代码结束后就结束主进程。

对比守护进程,代码执行完毕后立即关闭守护进程,因为在主进程看来代码执行完毕,主进程结束了,所以守护进程在代码结束后就被结束了。

守护线程会等待主线程的结束而结束。这是因为如果主线程结束意味着程序结束,主线程会一直等着所有非守护线程结束,回收资源然后退出程序,所以当所有非守护线程结束后,守护线程结束,然后主线程回收资源,结束程序。

下面对比守护进程和守护线程的示例:

4.1 先来看守护进程:

from multiprocessing import Processdef process_work(name):print(f"{name}")if __name__ == "__main__":p = Process(target=process_work,args=("守护进程"))p.daemon=Truep.start()print("主进程")# 打印内容如下
主进程

只打印了主进程,也就是说守护进程还没来得及执行程序就结束了。

4.2 再来看守护线程:

from threading import Threaddef thread_work(name):print(name)if __name__ == "__main__":t = Thread(target=thread_work,args=("守护线程",))t.daemo=Truet.start()print("\n主线程")# 打印内容如下
守护线程
主线程

也许你会说是由于线程太快了,所以才执行了守护线程。下面我们在线程中阻塞一段时间,在来看看会发生什么效果。

from threading import Thread
import time
def thread_work(name):time.sleep(3)  # 阻塞3秒print(name)if __name__ == "__main__":t = Thread(target=thread_work,args=("守护线程",))t.daemo=Truet.start()print("\n主线程")# 打印内容如下
主线程
守护线程

守护线程还是被执行了,如果是守护进程,守护进程里的代码是不会被执行的。


三、线程锁Lock

Lock也称线程同步锁,互斥锁,原子锁,该对象只有两个方法:

acquire(blocking=True, timeout=-1):加锁。

参数:

blocking:当为True时表示加锁,只允许一个线程执行被加锁的代码。直到遇到release()解锁后其它线程才可以执行加锁部分的代码。当为False时表示不加锁,并且不能调用release()否则会报RuntimeError。

timeout:设置加锁时间,单位为秒。-1表示一直等待线程release()后,才允许其它线程执行加锁的代码。

release():释放锁。

1、未加锁的代码示例

from threading import Thread
import timedef work():global ntemp = ntime.sleep(0.1)  # 由于线程太快了,所以这里停顿下n = temp -1if __name__ == "__main__":n = 100t_l = []for i in range(100):t = Thread(target=work,args=())t.start()t_l.append(t)for i in t_l:i.join()print(n)# 打印内容如下
99

在我们看来其实值应该是0的但却是99,就因为短暂停了0.1秒导致结果发生了变化。而我们这0.1秒的停留是模拟网络延迟或者进程调度等原因。造成了数据的结果的错乱。这个时候我们就需要线程锁来保证数据的安全性。

2、下面我们就通过给程序加锁,来保证数据的安全性:

from threading import Thread,Lock
import timedef work(lock):lock.acquire()  # 加锁global ntemp = ntime.sleep(0.1)  # 由于线程太快了,所以这里停顿下n = temp -1lock.release()  # 解锁if __name__ == "__main__":n = 100t_l = []lock = Lock()  # 得到一把锁对象for i in range(100):t = Thread(target=work,args=(lock,))t.start()t_l.append(t)for i in t_l:i.join()print(n)# 打印内容如下
0

我们会发现程序和上一个示例的运行效率上有着很大的差别。明显加锁后程序的运行效率降低了,我们管这种锁叫做线程同步锁,使原本并行的程序编程了串行所以程序的效率会慢很多,但是程序的运行结果是正确的。在程序的运行效率和数据的正确性,我们应首先确保程序的正确性然后在考虑程序的效率。


四、递归锁RLock

死锁与递归锁:

所谓死锁: 是指两个或两个以上的进程或线程在执行过程中,出现了一种互相等待的情况,它们都将无法进行下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程,如下模拟产生死锁。

from threading import Lock,Threaddef work():# 获取锁
    lock.acquire()print("我是工作线程 1")# 释放锁
    lock.release()def work_2():# 获取锁
    lock.acquire()print("我是工作线程 2")work()  # 调用工作线程1,造成死锁# 释放锁
    lock.release()if __name__ == '__main__':# 生成lock实例lock = Lock()# 开始3个线程for i in range(3):t = Thread(target=work_2)t.start()print('我是主线程')# 打印内容如下
我是主线程
我是工作线程 2  # 此时程序处于死锁状态

我们可以理解为一把锁对象只能创建一把锁,这把锁必须release后,才能再次使用。否则程序就会被锁住,等待解锁。如上面的代码,work_2执行创建一个lock锁,调用work又遇到一把lock锁,而在work_2中的lock锁没有被解锁,所以程序在work中等待lock解锁,最终造成了程序出现了死锁,下面我们使用递归锁RLock来解决上面的问题。

关于RLock的用法及方法一样,所以这里就不再重复了。

使用RLock避免死锁:

from threading import RLock,Threaddef work():# 获取锁
    lock.acquire()print("我是工作线程 1")# 释放锁
    lock.release()def work_2():# 获取锁
    lock.acquire()print("我是工作线程 2")work()  # 调用工作线程1,造成死锁# 释放锁
    lock.release()if __name__ == '__main__':# 使用递归锁RLocklock = RLock()# 开始3个线程for i in range(3):t = Thread(target=work_2)t.start()print('我是主线程')# 打印内容如下
我是工作线程 2
我是工作线程 1
我是工作线程 2
我是工作线程 1
我是工作线程 2
我是主线程
我是工作线程 1

使用递归锁后程序运行正常了。


五、条件对象Condition(lock=None)

Condition条件变量,与锁相关联,在实例化对象时可以给其传入一把锁,如果不传,会默认创建一把递归锁。目前我对它的就理解是它是一把带通知,挂起线程功能的锁。它可以挂起线程,然后发送通知激活线程,并且还可以加锁,属于一把高级锁,下面我们来看看常用的方法。

1、Condition类的方法

class threading.Condition([lock])

acquire():加锁、与Lock、RLock中的用法一致,这里不过多解释。

release():解锁、与Lock、RLock中的用法一致,这里不过多解释。

wait(timeout=None):挂起线程,如果timeout是None则必须等到notify或notify_all后线程才会被激活,并且被激活的线程会重新获取到一把锁,线程被激活后从wait挂起的位置继续向下执行。如果指定timeout超时时间,单位为秒,表示线程挂起一段时间后在继续执行。注意:如果调用线程在调用此方法时未获取锁,则会引发RuntimeError。

wait_for(predicate, timeout=None):这个不知道该如何解释。

notify(n=1):激活被挂起的线程,n表示激活n个被挂起的线程,注意:如果调用线程在调用此方法时未获取锁,则会引发RuntimeError。

notify_all():激活所有被挂起的线程,注意:如果调用线程在调用此方法时未获取锁,则会引发RuntimeError。。

2、下面是一个无聊的实例:

from threading import Condition,Thread
import timedef consume():'''消费者'''global cvglobal numcv.acquire()while True:num -= 1if num <= 0:print('-' * 20)cv.notify()  # 激活生产者线程
            cv.wait()print(f'消费数据-{num}')time.sleep(2)cv.release()def produce():'''生产者'''global cvglobal numcv.acquire()while 1:num += 1print(f'生产数据-{num}')if num >= 5:print('-' * 20)cv.notify()  # 激活消费者线程
            cv.wait()time.sleep(0.5)cv.release()if __name__ == '__main__':cv = Condition()   # 实例化条件对象num = 0# 开启线程produce_t = Thread(target=produce, args=())consume_t = Thread(target=consume, args=())produce_t.start()consume_t.start()


六、Threading类的方法:

threading.active_count():获取当前活动的线程对象数量。

threading.current_thread():获取当前的线程对象。如果调用方的控制线程不是通过线程模块创建的,则返回功能有限的虚拟线程对象。

threading.get_ident():获取线程标识符。

threading.enumerate():这个比较厉害,可以获取当前活动的所有线程对象的列表。该列表包括后台线程和使用current_thread()创建的虚拟线程。以列表的形式返回。

threading.main_thread():获取主线程对象。


关于Threading模块就简单介绍到这里吧。参考文档https://docs.python.org/3/library/threading.html#threading.Condition.notify

转载于:https://www.cnblogs.com/caesar-id/p/10764710.html

python线程(二)代码部分Threading模块相关推荐

  1. python线程池(threadpool)模块使用笔记详解

    这篇文章主要介绍了python线程池(threadpool)模块使用笔记详解,小编觉得挺不错的,现在分享给大家,也给大家做个参考.一起跟随小编过来看看吧 最近在做一个视频设备管理的项目,设备包括(摄像 ...

  2. python 测试 多线程 _thread和threading模块 线程同步,线程优先级队列

    文章目录 python 多线程简介 Python中使用线程的两种方式 1.函数式 示例 2.线程模块 示例 线程同步 示例 线程优先级队列( Queue)[暂时没用到,没仔细看] 示例 其他 thre ...

  3. 线程的创建 锁 Threading模块 事件 条件 定时器 队列 线程池 回调函数

    线程的创建: 创建线程的方式1: from threading import Thread import time def sayhi(name):time.sleep(2)print('%s say ...

  4. python线程池(threadpool)模块使用python2.7

    一.安装与简介 pip install threadpool pool = ThreadPool(poolsize) requests = makeRequests(some_callable, li ...

  5. Python网络与并发编程 10 threading模块线程锁

    前言 本章节将继续围绕threading模块讲解,基本上是纯理论偏多. 对于日常开发者来讲很少会使用到本章节的内容,但是对框架作者等是必备知识,同时也是高频的面试常见问题. 官方文档 线程安全 线程安 ...

  6. python使用threading模块实现多线程

    综述 Python这门解释性语言也有专门的线程模型,Python虚拟机使用GIL(Global Interpreter Lock,全局解释器锁)来互斥线程对共享资源的访问,但暂时无法利用多处理器的优势 ...

  7. Python:使用threading模块实现多线程(转)

    2019独角兽企业重金招聘Python工程师标准>>> Python:使用threading模块实现多线程(转) 分类: python   标签: thread    评论: 暂无评 ...

  8. python守护多线程_Python多线程Threading、子线程与守护线程实例详解

    线程Threading: python中多线程需要使用threading模块 线程的创建与运行: 1.直接调用threading的Thread类: 线程的创建:线程对象=thread.Thread(t ...

  9. python中的threading_python中的threading模块使用说明

    这段时间使用python做串口的底层库,用到了多线程,对这部分做一下总结.实际用完了后再回过头去看python的官方帮助文档,感觉受益匪浅,把里面的自己觉得有用的一些关键点翻译出来,留待后续查验. t ...

最新文章

  1. Python广度优先查找和深度优先查找(内附python教程分享)
  2. Android开启adb
  3. python自动化办公:邮件篇 (定时邮件问候女票so easy)
  4. jquery实时监听输入框值变化
  5. 京东java笔试_2017阿里,百度,京东java面试+笔试大合集,2018的你会吗?
  6. cordova自定义插件步骤
  7. 【Django 2021年最新版教程5】前端传递数据到后端处理 GET 方法
  8. opencv 安装及配置
  9. 汉字拼音首字母检索筛选数据
  10. 《Linux驱动:DM9000网卡驱动分析》
  11. 带左右箭头的图片轮播
  12. Why you should not shrink your data files
  13. EXCEL解析:使用poi解析xlsx和xls后缀的excel文件
  14. 财富管理技术服务商NewBanker完成千万级美元 Pre-C 轮融资
  15. 作为 IT 从业人员,你觉得有什么工具大大提高了你的工作效率?
  16. DFSGSDGHSDGSEDFG
  17. 配置SQL Server的ODBC数据源
  18. 7-1 求一元二次方程的根 (20 分)
  19. ZOJ 3587 Marlon's String
  20. (数据结构)树的双亲表示法

热门文章

  1. Android中加载事件的方式
  2. 二分搜索 HDOJ 2289 Cup
  3. 【网站开发】搭建一个属于自己的网站
  4. Win7下如何更改时间日期
  5. 史上最有趣的Readme
  6. iOS 处理键盘遮挡TextField、TextView问题
  7. NoClassDefFoundError: org/apache/flink/api/scala/typeutils/CaseClassTypeInfo
  8. linux下面的浏览器不停自动打开新网页
  9. 無法在 module_path 中找出佈景主題引擎:‘clearlooks’,
  10. quinlan的C4.5编译