python多线程threading模块
2019-06-07 本文已影响0人
mudssky
当我们要求程序并发或者需要执行多个独立的子任务的时候可以使用多线程
01.全局解释器锁GIL
目前python解释器同时只能执行一个线程,多线程环境中,只有一个线程能获得GIL,每个线程执行一段时间后释放GIL交给其他线程
因此,python的多线程只能利用cpu的一个核,GIL会在IO调用前被释放,适用于IO密集型任务。
02.退出线程
————在python中,你可以启动一个线程,但却无法停止它
当线程完成函数的执行时,它就会退出,或者调用thread.exit()之类的方法可以退出,但是不能像进程(kill pid)一样直接关闭。
03.threading模块
python中有thread和threading两个模块可以创建线程。thread功能简单,threading更高级,功能更全面更好用。
新手应该避免使用thread模块,python3中thread模块已经更名为_thread
如下
# python3.7
import thread
---------------------------------------------------------------------------
ModuleNotFoundError Traceback (most recent call last)
<ipython-input-3-e75c663b2a08> in <module>
----> 1 import thread
ModuleNotFoundError: No module named 'thread'
守护线程的概念:
避免使用_thread模块的一个原因是,_thread模块不支持守护线程,当主线程退出时,其他线程自动跟着退出,不论是否在工作
threading模块支持守护线程。可以为子线程设置守护线程标记,当标记值为True时(thread.daemon=true),表明该线程是不重要的。整个python程序会在所有非守护线程退出后才退出。也就是说使用threading模块不进行任何设置,python的主线程也不会突然结束。
使用Thread类创建线程,通常会有以下三种方式:
- 创建Thread的类的实例,传给它一个函数
- 创建Thread类的实例,传给它一个可调用的类实例
- 派生Thread的子类,并创建子类的实例
通常我们会选择1,3两种方式
下面用一个简单的例子方便理解
首先是创建Thread的实例,传给它一个函数的方法
# python3.7
import threading
import time
def test_thread(id,seconds):
print ('start thread',id,'at:',time.ctime())
time.sleep(seconds)
print('end thread',id,'at:',time.ctime())
def main():
sleep_time = [3,5,7,1,9]
# 存放线程的list
threads=[]
print('starting threads at',time.ctime())
# 创建线程的thread实例加入列表
for index,seconds in enumerate(sleep_time):
t = threading.Thread(target=test_thread,args=(index,seconds))
threads.append(t)
# 遍历列表,启动线程
for t in threads:
t.start()
# join方法作用是,让主线程等待该线程结束,所以join应该放在下面的循环里,放在这里就会导致这个启动线程的循环阻塞。结果就是顺序执行。
# join也可以设置超时时间
# join函数只在主线程只进行等待的时候有用,如果主线程还有其他事情,没必要调用join阻塞自己。
# t.join()
for t in threads:
t.join()
print('all threads finished')
if __name__=='__main__':
main()
starting threads at Fri Jun 7 15:02:27 2019
start thread 0 at: Fri Jun 7 15:02:27 2019
start thread 1 at: Fri Jun 7 15:02:27 2019
start thread 2 at: Fri Jun 7 15:02:27 2019
start thread 3 at: Fri Jun 7 15:02:27 2019
start thread 4 at: Fri Jun 7 15:02:27 2019
end thread 3 at: Fri Jun 7 15:02:28 2019
end thread 0 at: Fri Jun 7 15:02:30 2019
end thread 1 at: Fri Jun 7 15:02:32 2019
end thread 2 at: Fri Jun 7 15:02:34 2019
end thread 4 at: Fri Jun 7 15:02:36 2019
all threads finished
# 创建Thread的实例,传给它一个可调用的类的实例。
# python3.7
import threading
import time
class ThreadFunc(object):
def __init__(self,func,args,name=''):
self.name=name
self.func=func
self.args=args
def __call__(self):
# *作用是解包操作符,把参数元组分开成为一个个参数传递
self.func(*self.args)
def test_thread(id,seconds):
print ('start thread',id,'at:',time.ctime())
time.sleep(seconds)
print('end thread',id,'at:',time.ctime())
def main():
sleep_time = [3,5,7,1,9]
threads=[]
print('starting threads at',time.ctime())
for index,seconds in enumerate(sleep_time):
t = threading.Thread(target=ThreadFunc(test_thread,(index,seconds),test_thread.__name__))
threads.append(t)
# 遍历列表,启动线程
for t in threads:
t.start()
for t in threads:
t.join()
print('all threads finished')
if __name__=='__main__':
main()
starting threads at Fri Jun 7 15:08:02 2019
start thread 0 at: Fri Jun 7 15:08:02 2019
start thread 1 at:start thread 2 at: Fri Jun 7 15:08:02 2019start thread 3 at: Fri Jun 7 15:08:02 2019
Fri Jun 7 15:08:02 2019
start thread 4 at:
Fri Jun 7 15:08:02 2019
end thread 3 at: Fri Jun 7 15:08:03 2019
end thread 0 at: Fri Jun 7 15:08:05 2019
end thread 1 at: Fri Jun 7 15:08:07 2019
end thread 2 at: Fri Jun 7 15:08:09 2019
end thread 4 at: Fri Jun 7 15:08:11 2019
all threads finished
# 派生Thread的子类,并创建子类的实例
# python3.7
import threading
import time
class MyThread(threading.Thread):
def __init__(self,func,args,name=''):
threading.Thread.__init__(self)
self.name=name
self.func=func
self.args=args
def run(self):
# *作用是解包操作符,把参数元组分开成为一个个参数传递
self.func(*self.args)
def test_thread(id,seconds):
print ('start thread',id,'at:',time.ctime())
time.sleep(seconds)
print('end thread',id,'at:',time.ctime())
def main():
sleep_time = [3,5,7,1,9]
threads=[]
print('starting threads at',time.ctime())
for index,seconds in enumerate(sleep_time):
t = MyThread(test_thread,(index,seconds),test_thread.__name__)
threads.append(t)
# 遍历列表,启动线程
for t in threads:
t.start()
for t in threads:
t.join()
print('all threads finished')
if __name__=='__main__':
main()
starting threads at Fri Jun 7 15:17:06 2019
start thread 0start thread 1 at: Fri Jun 7 15:17:06 2019
at: Fri Jun 7 15:17:06 2019
start thread 2 at: Fri Jun 7 15:17:06 2019
start thread 3 at: Fri Jun 7 15:17:06 2019
start thread 4 at: Fri Jun 7 15:17:06 2019
end thread 3 at: Fri Jun 7 15:17:07 2019
end thread 0 at: Fri Jun 7 15:17:09 2019
end thread 1 at: Fri Jun 7 15:17:11 2019
end thread 2 at: Fri Jun 7 15:17:13 2019
end thread 4 at: Fri Jun 7 15:17:15 2019
all threads finished
可以看出,方式3和方式2的区别是,构造函数要调用基类的构造函数,call方法改为run方法
相比于传递函数的方式一,传递类可以封装一些参数和方法。