多线程编程
我们都知道,cpu调度和切换的最小单位是线程,进程是系统进行资源分配和调度的基本单位。因为进程对cpu的消耗较大,于是乎出现了线程,线程实际上是依赖于进程,一个进程下有多个线程。在io操作为主的情况下,多线程和多进程性能差别不大,线程的调度比进程的调度可以说是更轻量级的。
1.通过Thread类实例化
举一个不是很恰当的例子来帮助理解。。
我们习惯于吃饭有配汤,所以在做饭的时候也会做汤,当然如果做完饭再煮汤的话会很慢,对于吃货加懒人来说,在做饭的同时煮汤会节省大量的时间,而这个在做饭操作完成时等待饭熟的同时就去煮汤的过程实际上就是一个并发的操作。
import time
import threading
def cooking(rice):
print("start cooking")
time.sleep(5)
print("cooking finished")
def making_soup(water):
print("start making soup")
time.sleep(5)
print("soup finished")
if __name__ == "__main__":
thread_1 = threading.Thread(target=cooking, args=("",))
thread_2 = threading.Thread(target=making_soup, args=("",))
start = time.time()
thread_1.start()
thread_2.start()
print("Time spent: %s" % (time.time()-start))
结果如下
# start cooking
# start making soup
# Time spent: 0.0
# cooking finished
# soup finished
至此有个疑问,为什么time spent在两个线程运行中间且花费时间为0呢?
其实我们创建的并不只是两个线程,实际上是三个,另一个是两个线程的主线程,在start函数运行时两个线程开始运行,而主线程在其他代码(print花费时间代码段)运行。线程可以并行,所以在两个线程运行后,主线程并不等待两个线程运行结束就开始运行,当然这里有个重点,主线程在运行结束后它并没有退出(如果退出就不会打印最下面两行)。
####守护线程
如果我们必须要主线程退出的时候,把子线程全部杀掉该怎么做?我们可以借助setDaemon函数(守护线程函数)
if __name__ == "__main__":
thread_1 = threading.Thread(target=cooking, args=("",))
thread_2 = threading.Thread(target=making_soup, args=("",))
thread_1.setDaemon(True) # 参数传个True
thread_2.setDaemon(True)
start = time.time()
thread_1.start()
thread_2.start()
print("Time spent: %s" % (time.time()-start))
子线程就被kill掉了
# start cooking
# start making soup
# Time spent: 0.0009984970092773438
setDaemon函数实际上把两个线程设置为守护线程,在主线程关闭后,不关心守护线程的运行状态就将其关闭。
####join()
如果我们想要让子线程执行结束后在执行主线程该怎么做呢?
if __name__ == "__main__":
thread_1 = threading.Thread(target=cooking, args=("",))
thread_2 = threading.Thread(target=making_soup, args=("",))
start = time.time()
thread_1.start()
thread_2.start()
thread_1.join() # 在线程开始后加入join函数
thread_2.join()
print("Time spent: %s" % (time.time()-start))
# start cooking
# start making soup
# soup finished
# cooking finished
# Time spent: 5.001763343811035
join函数的作用是阻塞,主线程会等待被join函数作用的线程结束后再执行,而且从花费的时间来看,多个join下的线程实际上是同时进行的。
2.通过集成Thread来实现多线程
我们可以定义两个类对应上面的两个函数,让他们继承threading.Thread类,在类内定义run方法。在调用start的时候就可以开启进程
class Cooking(threading.Thread): # 继承threading.Thread类
def __init__(self, name):
super().__init__(name=name)
def run(self): # 必须得定义run方法, 方法和上面通过实例化内的代码一样
print("start cooking")
time.sleep(5)
print("cooking finished")
class Making_Soup(threading.Thread): # 继承threading.Thread类
def __init__(self, name):
super().__init__(name=name)
def run(self): # 必须得定义run方法, 方法和上面通过实例化内的代码一样
print("start making soup")
time.sleep(5)
print("soup finished")
if __name__ == "__main__":
thread_1 = Cooking('cooking')
thread_2 = Making_Soup('make_soup')
start = time.time()
thread_1.start()
thread_2.start()
thread_1.join()
thread_2.join()
# start cooking
# start making soup
# soup finished
# cooking finished
# Time spent: 5.0013391971588135
结果其实和上面的是一样的
一直拖拖拖,总算开始写了,比实际上要麻烦啊,希望能坚持下来