Python 简单理解多线程
Python中实现多线程需要使用到 threading 库,其中每一个 Thread类 的实例控制一个线程。
Thread类
#类签名
def __init__(self, group=None, target=None, name=None,
args=(), kwargs=None, *, daemon=None):
简单介绍一些初始化参数:
target: 指定线程由 run () 方法调用的可调用对象。默认为 None, 意味着不调用任何内容。
name: 指定该线程的名称。 在默认情况下,创建一个唯一的名称。
args: target调用的实参,元组格式。默认为 (),即不传参。
daemon: 为False表示父线程在运行结束时需要等待子线程结束才能结束程序,为True则表示父线程在运行结束时,子线程无论是否还有任务未完成都会跟随父进程退出,结束程序。
线程启动:
import threading def worker(arg):#线程执行的目标函数
print("I'm working {}".format(arg))
print("Fineshed") t = threading.Thread(target=worker,args=(threading.current_thread(),),name="firstworker")#线程对象
t.start()#启动线程 运行结果:
I'm working <_MainThread(MainThread, started 10936)>
Fineshed
上面例子中,当函数执行完之后,线程也就跟着退出了。
线程的传参:
import threading def add(x,y):
print(x+y) t = threading.Thread(target=add,args=(4,5))
t.start() print("====end===")
运行结果:
9
====end===
线程的传参和函数传参没有区别,只需要注意传入的必须为元祖格式。
线程退出:
如果线程中任务是无限循环语句,那这个线程将无法自动停止。
Python线程退出条件有以下几种:
1、线程内的函数语句执行完毕,线程自动结束
2、线程内的函数抛出未处理的异常
import threading
import time def worker(arg):
while True:
time.sleep(1)
print("I'm working {}".format(arg))
print("Fineshed") t = threading.Thread(target=worker,args=(threading.current_thread(),),name="firstworker")
t.start()
运行结果:
I'm working <_MainThread(MainThread, stopped 2468)>
I'm working <_MainThread(MainThread, stopped 2468)>
I'm working <_MainThread(MainThread, stopped 2468)>
...
上面例子中,线程启动后,将一直循环下去,线程不会自动退出。
import threading
import time def worker(arg):
count = 0
while True:
if count > 5:
raise RuntimeError(count)
time.sleep(1)
print("I'm working {}".format(arg))
count += 1
print("Fineshed") t = threading.Thread(target=worker,args=(threading.enumerate(),))
t.start() print("====end===") 运行结果:
====end===
I'm working [<_MainThread(MainThread, stopped 10992)>]
I'm working [<_MainThread(MainThread, stopped 10992)>]
I'm working [<_MainThread(MainThread, stopped 10992)>]
I'm working [<_MainThread(MainThread, stopped 10992)>]
I'm working [<_MainThread(MainThread, stopped 10992)>]
I'm working [<_MainThread(MainThread, stopped 10992)>]
Exception in thread Thread-1:
Traceback (most recent call last):
File "C:/python/test.py", line 8, in worker
raise RuntimeError(count)
RuntimeError: 6
上面例子中,演示了触发异常自动退出线程。但最先打印的是主程序的"===end==="语句,是因为在程序中,主线程启动一个线程后,不会等待子线程执行完毕,就继续执行了后续语句,在执行完主线程语句后,发现还有子线程没有结束,于是等待子线程执行结束,子线程在运行时抛出了未处理的异常,最终子线程结束,主线程也随之结束。这里需要了解daemon线程和non-daemon线程,稍后就会介绍。
threading属性:
threading.current_thread() 返回当前线程对象
threading.main_thread() 返回主线程对象
threading.active_count() 返回处于Active状态的线程个数
threading.enumerate() 返回所有存活的线程的列表,不包括已经终止的线程和未启动的线程
threading.get_ident() 返回当前线程的ID,非0整数
举例:
import threading
import time def showthreadinfo():
print("current thread = {}".format(threading.current_thread()))
print("main thread = {}".format(threading.main_thread()))
print("active thread count = {}".format(threading.active_count()))
print("active thread list = {}".format(threading.enumerate()))
print("thread id = {}".format(threading.get_ident()))
print("~~~~~~~~~~~~~") def add(x,y):
time.sleep(1)
showthreadinfo() #子线程中调用
print(x+y) showthreadinfo() #主线程中调用
time.sleep(1) t = threading.Thread(target=add,args=(4,5))
t.start() print("====end===") 运行结果:
current thread = <_MainThread(MainThread, started 192)>
main thread = <_MainThread(MainThread, started 192)>
active thread count = 1
active thread list = [<_MainThread(MainThread, started 192)>]
thread id = 192
~~~~~~~~~~~~~
====end===
current thread = <Thread(Thread-1, started 8424)>
main thread = <_MainThread(MainThread, stopped 192)>
active thread count = 2
active thread list = [<_MainThread(MainThread, stopped 192)>, <Thread(Thread-1, started 8424)>]
thread id = 8424
~~~~~~~~~~~~~
9
上面例子中,在主线程中只能看到存活的只有自己,因为子线程还没有启动,且它的父线程就是它自己。子线程启动时,它的名字为Thread-1,这个名字是解释器自动命名的,如果定义线程对象时添加了name="threadName",则这里显示的就是threadName;同时,子线程的父线程就是主线程,也就是说谁启动的线程谁就是它的父线程;子线程能看到的存活线程有父线程和自身。
Thread实例的属性:
threading.current_thread().name 线程名,只是一个标识符,可以使用getName()、setName()获取和运行时重命名。
threading.current_thread().ident 线程ID,非0整数。线程启动后才会有ID,否则为None。线程退出,此ID依旧可以访问。此ID可以重复使用
threading.current_thread().is_alive() 返回线程是否存活,布尔值,True或False。
举例:
import threading
import time def worker():
count = 1
while True:
if count >= 6:
break
time.sleep(1)
count += 1
print("thread name = {}".format(threading.current_thread().name)) t = threading.Thread(target=worker,name="MyThread")
t.start() while True:
time.sleep(1.1)
if t.is_alive():
print("{} {} alive".format(t.name,t.ident))
else:
print("{} {} alive".format(t.name, t.ident))
t.start() print("====end===") 运行结果:
thread name = MyThread
MyThread 9400 alive
thread name = MyThread
MyThread 9400 alive
thread name = MyThread
MyThread 9400 alive
thread name = MyThread
MyThread 9400 alive
thread name = MyThread
MyThread 9400 alive
Traceback (most recent call last):
File "C:/python/test.py", line 22, in <module>
t.start()
raise RuntimeError("threads can only be started once")
RuntimeError: threads can only be started once
从上面例子中可以看到子线程存活时的名字和线程ID,但在线程退出后,尝试再次启动线程时,抛出RuntimeError异常,表明线程对象在定义后只能启动一次。
举例 getName()和setName():
import threading
import time def add(x,y):
for _ in range(5):
time.sleep(1)
print("x+y={}".format(x+y)) t = threading.Thread(target=add,name="MyThread",args=(6,7))
t.start() while True:
time.sleep(1)
if t.is_alive():
print("{} {} alive".format(t.name,t.ident))
print("Thread name",t.getName())
t.setName("MyThreadTwo")
else:
print("{} {} alive".format(t.name, t.ident))
print("Thread abort....")
break
# t.start() print("====end===") 运行结果:
MyThread 2564 alive
Thread name MyThread
x+y=13
MyThreadTwo 2564 alive
Thread name MyThreadTwo
x+y=13
MyThreadTwo 2564 alive
Thread name MyThreadTwo
x+y=13
MyThreadTwo 2564 alive
Thread name MyThreadTwo
x+y=13
MyThreadTwo 2564 alive
Thread name MyThreadTwo
x+y=13
MyThreadTwo 2564 alive
Thread abort....
====end===
上面例子演示了在运行时获取线程名和重命名线程名。
线程的start()和run()方法:
start():
import threading
import time def add(x,y):
for _ in range(5):
time.sleep(0.5)
print("x+y={}".format(x+y)) class MyThread(threading.Thread):
def start(self):
print('start~~~~~~~~~~')
super().start() def run(self):
print('run~~~~~~~~~~~~')
super().run() #调用父类的start()和run()方法 t = MyThread(target=add,name="MyThread",args=(6,7))
t.start()
# t.run()
print("====end===") 运行结果:
start~~~~~~~~~~
run~~~~~~~~~~~~
====end===
x+y=13
x+y=13
x+y=13
x+y=13
x+y=13
从上面的例子中,可以看出start()方法会先运行start()方法,再运行run()方法。
跟进一下start() 方法源码中的调用过程:
1、def start(self):
_start_new_thread(self._bootstrap, ())
.... 2、_start_new_thread = _thread.start_new_thread 3、def start_new_thread(function, args, kwargs=None):
pass 4、def _bootstrap(self):
self._bootstrap_inner() 5、def _bootstrap_inner(self):
....
try:
self.run()#最终start()方法调用了run()方法
except SystemExit:
pass
从上面跟踪源码的过程大概了解了start()方法如何调用到了run()方法。
run()方法:
import threading
import time def add(x,y):
for _ in range(5):
time.sleep(0.5)
print("x+y={}".format(x+y)) class MyThread(threading.Thread):
def start(self):
print('start~~~~~~~~~~')
super().start() def run(self):
print('run~~~~~~~~~~~~')
super().run() #调用父类的start()和run()方法 t = MyThread(target=add,name="MyThread",args=(6,7))
# t.start()
t.run()
print("====end===") 运行结果:
run~~~~~~~~~~~~
x+y=13
x+y=13
x+y=13
x+y=13
x+y=13
====end===
上面例子中,运行线程的run()方法只能调用到run()方法。
跟踪一下run() 方法在源码中的调用过程:
1、def __init__(self, group=None, target=None, name=None,
args=(), kwargs=None, *, daemon=None):
self._target = target
self._args = args
self._kwargs = kwargs
.... 2、def run(self):
if self._target:
self._target(*self._args, **self._kwargs)
....
可以看出,_target是我们传入的目标函数,run()方法其实就类似一个装饰器,最终还是将_args 和_kwargs 参数传入目标函数运行,返回结果。
start() --> run() --> _target()
run() --> _target()
上面两个例子简单介绍了start()方法和run()方法的调用,下一篇文章再详细看一下它们到底有什么区别。
总结:
本文主要介绍了: Thread类、线程启动、线程的传参、线程退出、threading属性、Thread实例的属性、举例getName()和setName()、线程的start()和run()方法
Python 简单理解多线程的更多相关文章
- Python简单的多线程demo:常用写法
简单多线程实现:启动50个线程,并计算执行时间. import threading import time def run(n): time.sleep(3) print("task:&qu ...
- Python简单的多线程demo:装逼写法
用面向对象来写多线程: import threading class MyThread(threading.Thread): def __init__(self, n): super(MyThread ...
- Python 简单的多线程聊天
# client 端 import socket ip_port = ('127.0.0.1', 8091) sk = socket.socket() sk.connect(ip_port) prin ...
- python 简单搭建阻塞式单进程,多进程,多线程服务
由于经常被抓取文章内容,在此附上博客文章网址:,偶尔会更新某些出错的数据或文字,建议到我博客地址 : --> 点击这里 我们可以通过这样子的方式去理解apache的工作原理 1 单进程TCP服 ...
- 一个简单的多线程Python爬虫(一)
一个简单的多线程Python爬虫 最近想要抓取拉勾网的数据,最开始是使用Scrapy的,但是遇到了下面两个问题: 前端页面是用JS模板引擎生成的 接口主要是用POST提交参数的 目前不会处理使用JS模 ...
- python装饰器三种装饰模式的简单理解
学设计模式中有个装饰模式,用java实现起来不是很难,但是远远没有python简单,难怪越来越火了! 这里就简单讨论下python的几种装饰模式: 一 无参装饰器: # 装饰器 import time ...
- python 简单搭建非阻塞式单进程,select模式,epoll模式服务
由于经常被抓取文章内容,在此附上博客文章网址:,偶尔会更新某些出错的数据或文字,建议到我博客地址 : --> 点击这里 可以看我的上篇文章 <python 简单搭建阻塞式单进程,多进程, ...
- python并发编程&多线程(二)
前导理论知识见:python并发编程&多线程(一) 一 threading模块介绍 multiprocess模块的完全模仿了threading模块的接口,二者在使用层面,有很大的相似性 官网链 ...
- Python中的多线程编程,线程安全与锁(二)
在我的上篇博文Python中的多线程编程,线程安全与锁(一)中,我们熟悉了多线程编程与线程安全相关重要概念, Threading.Lock实现互斥锁的简单示例,两种死锁(迭代死锁和互相等待死锁)情况及 ...
随机推荐
- iOS中单例需要注意的
单例模式怎么定义的,可能在不同的语言,不同的书中不完全一样,但是概况开来都应该是:一个类有且仅有一个实例,并且自行实例化向整个系统提供. 因此,首先你可能需要确定你是真的需要一个单例类,还是说仅仅是需 ...
- Appium python自动化测试系列之appium环境搭建(二)
2.1 基础环境搭建 当我们学习新的一项技术开始基本都是从环境搭建开始,本书除了第一章节也是的,如果你连最基础的环境都没有那么我们也没必要去说太多,大概介绍一下: 1.因为appium是支持andr ...
- BAT级别分类
阿里的级别:P为技术岗,M为管理岗.P7是技术专家级别. 阿里级别对应薪资: 百度使用的T系列及对应薪资: 腾讯的T系列及对应薪资:
- 如何在 UWP 使用 wpf 的 Trigger
本文需要告诉大家,如何使用 Behaviors 做出 WPF 的 Trigger ,需要知道 UWP 不支持 WPF 的 Trigger . 安装 Behaviors 请使用 Nuget 安装,可以输 ...
- win10 uwp 屏幕常亮
我们在播放视频需要屏幕常亮,我们可以使用DisplayRequest,因为代码简单我直接写,代码来自https://msdn.microsoft.com/en-us/library/windows/a ...
- UWP取出图片主色调
一切都要从风车动漫的新详情页说起... 当我最初拿到风车动漫新详情页的UI设计概念图时,新详情页中有两点: 1.图片的高斯模糊 2.取出图片的主色调(主要用于tag和相关动漫的标题背景) 大概就是要这 ...
- Java常用类(三)之StringBuffer与StringBuidler
前言 前面一篇给大家介绍了String类,这个我们经常会用到的一个类,那这一篇给大家分享的是StringBuffer与StringBuidler.等下我也会比较他们三个之间的区别 一.StringBu ...
- springCloud四:熔断器ribbon--Hystrix
注:前文概念部分摘抄自博客园 纯洁的微笑 熔断器 雪崩效应 在微服务架构中通常会有多个服务层调用,基础服务的故障可能会导致级联故障,进而造成整个系统不可用的情况,这种现象被称为服务雪崩效应.服务雪崩 ...
- Java基础总结--数组
---数组的定义---组织存储一组数据1.存放相同类型数据的集合--就是一种容器本质上变量也是一种容器--区别就是只存储了一个数据的容器--面对容器,而不是分散的数据eg.买一盘鸡蛋--蛋托其实就是容 ...
- Chrome 62 的大坑:修改密码后始终使用保存的旧密码登录
最近有用户向我们反馈,修改密码后,怎么也登录不了我们网站,总是提示密码错误.用户确认密码肯定没错,通过用户发给我们的操作截图看,用户修改密码的操作也没问题. 开始我们没能重现出这个问题,我们检查了相关 ...