前言
在这里记录一下我学习多线程的一些笔记
1.多线程简介
多线程类似于同时执行多个不同程序,多线程运行有如下优点:
- 使用线程可以把占据长时间的程序中的任务放到后台去处理.
- 用户界面可以更加吸引人
- 程序运行的时间可能加快
- 在一些等待的任务的实现中如:用户输入,文件读写和网络收发数据等,线程就比较有用了.
注:主程序是主线程,程序中创建的线程都是子线程
2.python实现多线程
我们来通过一个简单的程序来学习一下多线程.
先来看一个不用多线程的程序:test_1.py:
import time
def func(n):
start_time = time.time()
print('task',n)
time.sleep(2)
end_time = time.time()
print('任务{}所用时间为:{}'.format(n,(end_time-start_time)))
if __name__ == '__main__':
start_time = time.time()
for i in range(2):
func(i+1)
end_time = time.time()
print('程序所花时间为:',end_time-start_time)
执行结果:
task 1
任务1所用时间为:2.0012800693511963
task 2
任务2所用时间为:2.0009312629699707
程序所花时间为: 4.002368211746216
可以看出,每个任务都花费2秒左右的时间,程序总共话费时间为4秒左右.这个我们也很好理解,因为程序是串行的,只有第一个任务完成后才能完成第二个任务.
接下来我们看一下使用多线程后的效果:threading_1.py
import time
import threading
def func(n):
start_time = time.time()
print('task',n)
time.sleep(2)
end_time = time.time()
print('任务{}所用时间为:{}'.format(n,(end_time-start_time)))
if __name__ == '__main__':
start_time = time.time()
for i in range(2):
t = threading.Thread(target=func,args=(i+1,))
t.start()
end_time = time.time()
print('程序所花时间为:',end_time-start_time)
执行结果:
task 1
task 2
程序所花时间为: 0.0005745887756347656
任务2所用时间为:2.002145528793335
任务1所用时间为:2.0024964809417725
我们惊讶的发现,竟然是主程序执行完成以后又执行了两个子程序.为什么会出现这种情况呢?
出现这种情况的原因就是:每个线程都是独立且[平等的,python执行多线程的时候其实是按着上下文切换的原则来执行每个线程的,当遇到IO操作时,会自动切换另一个线程.在该程序中,当task 1和task 2执行完print()操作后遇到了time.sleep(2)操作,所以此时就会切换到主线程,主线程一路畅通,执行到最后(主线程也就是主函数),当线程执行完成后,再回去执行没有执行完的线程.那么我们能不能让所有的子线程都执行完后再执行主线程呢?答案是可以的,我们来看下面这段程序.
import time
import threading
def func(n):
start_time = time.time()
print('task',n)
time.sleep(2)
end_time = time.time()
print('任务{}所用时间为:{}'.format(n,(end_time-start_time)))
if __name__ == '__main__':
start_time = time.time()
thread_list = []
for i in range(2):
t = threading.Thread(target=func,args=(i+1,))
t.start()
thread_list.append(t)
for t in thread_list:
t.join()
end_time = time.time()
print('程序所花时间为:',end_time-start_time)
执行结果:
task 1
task 2
任务2所用时间为:2.0020792484283447
任务1所用时间为:2.002394437789917
程序所花时间为: 2.0028533935546875
我们可以看到,此时就可以实现并发的效果了.每个子线程执行时间为2秒,程序都执行完后也是2秒.
程序解释:
(1)我们要实现多线程,首先要导入python多线程模块:threading
import threading
(2)创建一个函数,用于测试多线程:
def func(n):
start_time = time.time()
print('task',n)
time.sleep(2)
end_time = time.time()
print('任务{}所用时间为:{}'.format(n,(end_time-start_time)))
这个函数我们就不做过多解释了.
(3)创建线程对象:
t = threading.Thread(target=func,args=(i+1,))
注:创建多线程时使用threading.Thread(),其中,target是要运行的函数,不能加括号,args是要传入的参数,这里的args接收的是一个元组,所以,我们即便是只有一个参数,也要加上逗号.
(4)启动线程
t.start()
(5)等待子线程终止
t.join()
(6)等待主线程执行完.
如果看不出多线程的效果,我们来看下面这个例子.
import time
import threading
def func(n):
start_time = time.time()
print('task',n)
time.sleep(2)
end_time = time.time()
print('任务{}所用时间为:{}'.format(n,(end_time-start_time)))
if __name__ == '__main__':
start_time = time.time()
thread_list = []
for i in range(20):
t = threading.Thread(target=func,args=(i+1,))
t.start()
thread_list.append(t)
for t in thread_list:
t.join()
end_time = time.time()
print('程序所花时间为:',end_time-start_time)
我们启动了20个子任务,如果按照串行的方式来运行程序,每个子任务需要2秒,20个子任务需要40秒完成,但我们使用了多线程后,可以实现40个子任务并行的效果,最终只需要2秒就可以完成任务,来看执行结果:
task 1
task 2
task 3
task 4
task 5
task 6
task 7
task 8
task 9
task 10
task 11
task 12
task 13
task 14
task 15
task 16
task 17
task 18
task 19
task 20
任务1所用时间为:2.0002286434173584
任务4所用时间为:2.000516653060913
任务6所用时间为:2.001020669937134
任务11所用时间为:2.0004289150238037
任务12所用时间为:2.0003764629364014
任务3所用时间为:2.0015926361083984
任务5所用时间为:2.001516819000244
任务9所用时间为:2.0010743141174316
任务7所用时间为:2.001377582550049
任务8所用时间为:2.0015039443969727
任务2所用时间为:2.0023436546325684
任务15所用时间为:2.0006370544433594
任务14所用时间为:2.000804901123047
任务10所用时间为:2.001779317855835
任务20所用时间为:2.000225782394409
任务16所用时间为:2.001055955886841
任务13所用时间为:2.0022807121276855
任务17所用时间为:2.0021753311157227
任务19所用时间为:2.0018365383148193
任务18所用时间为:2.0020861625671387
程序所花时间为: 2.0048460960388184
可能会有人问,既然 t.join()是等待子线程结束,那为什么不 t.start()后直接 t.join()呢?
我们来看下面的程序:
import threading
import time
# 定义函数,用来测试多线程
def func(n):
start_time = time.time()
print('task',n)
time.sleep(2)
end_time = time.time()
print('任务{}所用时间为:{}'.format(n,(end_time-start_time)))
class MyThread(threading.Thread): # 创建自己的多线程类,继承threading.Thread
def __init__(self,thread_id,thread_name,count):
super(MyThread, self).__init__() # 重构父类__init__()函数,要继承父类
self.thread_id = thread_id
self.thread_name = thread_name
self.count = count
def run(self): # 要将线程执行的任务写在run方法中.
print('开始线程:',self.name)
func(self.count)
print('退出线程:',self.name)
if __name__ == '__main__':
start_time = time.time()
for i in range(6):
t = MyThread(i,'thread-'+str(i),i+1) # 实例化线程
t.start() # 启动线程
t.join()
end_time = time.time()
print('程序所用时间为:',end_time-start_time)
直接来看结果:
开始线程: Thread-1
task 1
任务1所用时间为:2.002120018005371
退出线程: Thread-1
开始线程: Thread-2
task 2
任务2所用时间为:2.0021109580993652
退出线程: Thread-2
开始线程: Thread-3
task 3
任务3所用时间为:2.002117872238159
退出线程: Thread-3
开始线程: Thread-4
task 4
任务4所用时间为:2.0021157264709473
退出线程: Thread-4
开始线程: Thread-5
task 5
任务5所用时间为:2.00205659866333
退出线程: Thread-5
开始线程: Thread-6
task 6
任务6所用时间为:2.0020573139190674
退出线程: Thread-6
程序所用时间为: 12.01600456237793
可以看到,这样的效果其实就是线程串行,这样就失去了多线程的意义.
3.使用类创建多线程
我们还可以使用创建类的形式创建多线程.
import threading
import time
# 定义函数,用来测试多线程
def func(n):
start_time = time.time()
print('task',n)
time.sleep(2)
end_time = time.time()
print('任务{}所用时间为:{}'.format(n,(end_time-start_time)))
class MyThread(threading.Thread): # 创建自己的多线程类,继承threading.Thread
def __init__(self,thread_id,thread_name,count):
super(MyThread, self).__init__() # 重构父类__init__()函数,要继承父类
self.thread_id = thread_id
self.thread_name = thread_name
self.count = count
def run(self): # 要将线程执行的任务写在run方法中.
print('开始线程:',self.name)
func(self.count)
print('退出线程:',self.name)
if __name__ == '__main__':
start_time = time.time()
thread_list = []
for i in range(6):
t = MyThread(i,'thread-'+str(i),i+1) # 实例化线程
t.start() # 启动线程
thread_list.append(t)
for t in thread_list:
t.join() # 等待子线程终止
end_time = time.time()
print('程序所用时间为:',end_time-start_time)
执行结果:
开始线程: Thread-1
开始线程: Thread-2
task 1
task 2
开始线程: Thread-3
task 3
开始线程: Thread-4
task 4
开始线程: Thread-5
task 5
开始线程: Thread-6
task 6
任务2所用时间为:2.0006625652313232
退出线程: Thread-2
任务1所用时间为:2.002107858657837
退出线程: Thread-1
任务3所用时间为:2.0011298656463623
退出线程: Thread-3
任务6所用时间为:2.0014195442199707
任务4所用时间为:2.0017075538635254
退出线程: Thread-4
退出线程: Thread-6
任务5所用时间为:2.002039670944214
退出线程: Thread-5
程序所用时间为: 2.0067389011383057
使用类创建多线程时要注意以下几点:
- 要继承threading.Thread类
- 要将多线程执行的过程写在run()方法中.
4.多线程函数总结
我们在之前用到了多线程的 start()函数,join()函数以及run()函数,这里我们就来总结一下多线程中的函数.
(1)创建线程 threading.Thread:
t = threading.Thread(target = func,args = (n,))
(2)启动线程:
t.start()
(3)阻塞子线程,等待子线程结束后再往下执行:
t.join()
(4)run()和start()的区别:
start()方法是启动一个子线程
run()方法并不启动一个新线程,只是在主线程中调用了一个普通函数而已.
t.start()的时候实际上是执行的run()函数中的流程.
(5)返回当前的线程变量:
threading.current_thread()
(6)返回正在运行的线程数量:
threading.active_count()
写在最后
本文是个人的一些学习笔记,如有侵权,请及时联系我进行删除,谢谢大家.