赞
踩
Python 3.X实现多线程的是threading模块,使用它可以创建多线程程序,并且在多线程间进行同步和通信。因为是一个模块,所以使用前必须先导入:
import threading
Python支持两种创建多线程的方式:
• 通过threading.Thread()创建。
• 通过继承threading.Thread类创建。
Thread()的语法如下:
threading.Thread(group=None, target=None, name=None, args=(), kwargs={},daemon=None)
group:必须为None,与ThreadGroup类相关,一般不使用。
target:线程调用的对象,就是目标函数。
name:为线程起个名字。默认是Thread-x,x是序号,由1开始,第一个创建的线程名字就是Thread-1。
args:为目标函数传递实参,元组。
kwargs:为目标函数传递关键字参数,字典。
daemon:用来设置线程是否随主线程退出而退出。
参数虽然很多,但是实际常用的是target和args,可以用例子1来做演示。
例1
- import threading#导入模块
-
- def test(x,y):#定义测试函数
- for i in range(x,y):
- print(i)
- thread1=threading.Thread(name='t1',target=test,args=(1,10))
- thread2=threading.Thread(name='t2',target=test,args=(11,20))
- thread1.start() #启动线程1
- thread2.start() #启动线程2
第8~9行的start()函数用来启动线程。如果按照先执行一段代码再执行一段代码的传统形式,那么上述代码应该是先输出1~10再输出11~20,但是运行程序后效果却如下图所示
再运行一次,发现结果变了
这是因为两个线程会并发运行,所以结果不一定每次都是顺序的1~10,这是根据CPU给两个线程分配的时间片段来决定的。多运行几次代码,就会发现每次效果都有所不同。
- import threading#导入模块
-
- class mythread(threading.Thread):#继承threading.Thread类
- def run(myself):#定义测试函数
- for i in range(1,10):
- print(i)
- thread1=mythread()
- thread2=mythread()
- thread1.start() #启动线程1
- thread2.start() #启动线程2
第3行自定义一个类继承自threading.Thread,然后重写父类的run()方法,会在线程启动时(执行start())自动执行。如果把第10~11行的start()换为run(),就会发现run()仅仅是被当作一个普通的函数使用。只有在线程start时,它才是多线程的一种调用函数。也就是不存在并行,只会串行地执行。
在Python中,主线程是第一个启动的线程。我们需要了解两个概念:
父线程:如果线程A中启动了一个线程B,那么A就是B的父线程。
子线程:如果线程A中启动了一个线程B,那么B就是A的子线程。
创建线程时有一个daemon属性,可以用来判断主线程。当daemon设置为False时,子线程不会随主线程退出而退出,主线程会一直等着子线程执行完。当daemon设置为True时,当主线程结束,其他子线程就会被强制结束。
使用daemon属性时有以下几个注意事项:
daemon属性必须在start()之前设置,否则会引发RuntimeError异常。
每个线程都有daemon属性,可以显式设置,也可以不设置(取默认值None)。
如果子线程不设置daemon属性,就取当前线程的daemon来设置。子线程继承子线程的daemon值,作用和设置None一样。
从主线程创建的所有线程不设置daemon属性,默认都是daemon=False。
为了演示主线程的例子,我们需要学习一个time模块中的sleep()函数(用于推迟线程的执行,默认时间是秒)。下面引入time模块来演示例子
- import threading#导入模块
- import time
-
- def test():
- time.sleep(3)
- for i in range(10):
- print(i)
-
- thread1=threading.Thread(target=test,daemon=False)
- thread1.start()
-
- print('主线程完成了')
上述代码的执行结果如图
当主线程完毕时,子线程依然会执行,就是输出0~9。如果将第9行的daemon=False改为daemon=True,那么程序应该只输出“主线程完成了”,因为主线程完成后会强制子线程退出,但实际效果却与上图一致,这又是为什么呢?
原来这样的测试并不适用于IDLE环境中的交互模式或脚本运行模式,因为在该环境中的主线程只有在退出Python IDLE时才终止。
多线程提供了一个方法join(),简单来说是一个阻塞线程。在一个线程中调用另一个线程的join()方法,调用者将被阻塞,直到被调用线程终止。其语法是:
join(timeout=None)
timeout参数指定调用者等待多久,没有设置时就一直等待被调用线程结束。其中,一个线程可以被join多次。
- import threading#导入模块
- import time
-
- def test():
- time.sleep(3)
- for i in range(10):
- print(i)
-
- thread1=threading.Thread(target=test,daemon=False)
- thread1.start()
- print('主线程完成了')
前面学习daemon属性时已经提到,当取默认值或者设置为False时,主线程退出后子线程依然会执行。因为子线程当时设置了sleep(),所以先执行了主线程的print输出,然后才输出0~9。此时,如果在start()行后面添加如下join()方法:
thread1.join()
这样在输出时,主线程会等到输出0~9后再执行自己的print输出,效果如图
除了前面介绍的join(),其实threading.Thread类还提供了很多方法,主要方法参见表
run()、start()、join()在前面都介绍过,其他3个方法可以用例子来说明。
isAlive()、getName()、setName()的例子
- import threading#导入模块
- import time
-
- def test():
- time.sleep(6)
- for i in range(10):
- print(i)
-
- thread1=threading.Thread(target=test)
- print('1.当前线程是否是活动的:',thread1.isAlive())
- thread1.start()
- print('2.当前线程是否是活动的:',thread1.isAlive())
- print('当前线程',thread1.getName())
- thread1.join()
- print('线程完毕')
在第10行时,因为还没有使用start()启动线程,所以当前线程不是活动的状态。执行到第12行时就输出了True。第13行获取线程的名称,因为创建线程时没有使用name属性,所以线程的默认名字是Thread-x这种形式。
在代码运行期间,也可以使用setName()更改线程的名字。下面修改代码为例子
- import threading#导入模块
- import time
-
- def test():
- time.sleep(6)
- for i in range(10):
- print(i)
-
- thread1=threading.Thread(target=test)
- print('1.当前线程是否是活动的:',thread1.isAlive())
- thread1.start()
- print('2.当前线程是否是活动的:',thread1.isAlive())
- thread1.setName('Th-1')
- print('当前线程',thread1.getName())
- thread1.join()
- print('线程完毕')
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。