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 = -1, started 8424)>
- main thread = <_MainThread(MainThread, stopped 192)>
- active thread count = 2
- active thread list = [<_MainThread(MainThread, stopped 192)>, -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
- 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() 方法
来源: http://www.bubuko.com/infodetail-2784996.html