一、多线程&多进程
对于操作系统来说,一个任务就是一个进程。比如我在电脑上打开视频看电视,再启动QQ,这样打开视频和启动QQ就是两个进程了 。进程是多个资源的集合
每个进程中可以做很多事情,比如我打开QQ,可以与A打字聊天,同时还可以与B视频,接收C的文件,一个进程中可以有很多线程来干活,这样一个QQ需要运行多个子任务,我们把这些子任务叫做 线程(thread)
每个进程中至少有一个线程在干活,比如我打开QQ,即使不做任何操作,还是保留了一个QQ窗口,这就是主线程,子线程会等着主线程来调
我们在做事情的时候,一个人做是比较慢的,如果多个人一起来做的话,就比较快了,程序也是一样的,我们想运行的速度快一点的话,就得使用多进程,或者多线程,在python里面,多线程被很多人诟病,为什么呢,因为Python的解释器使用了GIL的一个叫全局解释器锁,它不能利用多核CPU,
只能运行在一个cpu上面,但是你在运行程序的时候,看起来好像还是在一起运行的,是因为操作系统轮流让各个任务交替执行,任务1执行0.01秒,切换到任务2,任务2执行0.01秒,再切换到任务3,执行0.01秒……这样反复执行下去。
表面上看,每个任务都是交替执行的,但是,由于CPU的执行速度实在是太快了, 我们感觉就像所有任务都在同时执行一样。这个叫做上下文切换。
二、python中通过threading模块来实现多线程
简单启动线程的例子
import threading import time def sayhi(num): #定义每个线程要运行的函数 print("running on number:%s" %num) time.sleep(3) if __name__ == '__main__': t1 = threading.Thread(target=sayhi,args=(1,)) #生成一个线程实例 t2 = threading.Thread(target=sayhi,args=(2,)) #生成另一个线程实例 t1.start() #启动线程 t2.start() #启动另一个线程
主线程,也就是程序一开始运行的时候,最初的那个线程
子线程,通过thread类实例化的线程,都是子线程
如果加了线程等待,主线程等待子线程,执行结束后,主线程再去做别的操作。否则主线程在启动子线程后,不会等待子线程结束,而是继续走主线程代码
下面举例说明两种线程等待的方式
import threading import time def insert_db(): time.sleep(3) print('insert_db over') #串形的方式,非多线程 # start_time = time.time() # for i in range(3): # insert_db() # end_time = time.time() # print('串行的执行的时间',end_time - start_time ) threads = [] start_time2 = time.time() #1、第一种方式,麻烦一点 # for i in range(3): # t = threading.Thread(target=insert_db)#有入参时,需要加args=()或者args=[]来传递参数 # t.start() # threads.append(t) # # for t in threads: # t.join()#使用join实现主线程等待子线程结束 #2、第二种方式,思路是判断当前存活的线程个数 for i in range(3): t = threading.Thread(target=insert_db) t.start() #当线程数为1,即子线程结束,代码才往下走,否则在死循环中 while threading.activeCount()!=1:#活着的线程数 pass end_time2 = time.time() print('多线程执行的时间',end_time2 - start_time2)
守护线程:当主线程结束后,守护线程会自动结束。就比如秦始皇死后,所有为他建造皇陵的人全部都要去陪葬。那么秦始皇就是主线程,其他陪葬的都是子线程。
以一个视频聊天为例,关闭QQ后,所有的子线程会停止
#守护线程,一但主线程死掉,那么守护线程不管有没有执行完成,全都结束 import threading import time def talk(name): print('正在和%s聊天'%name) time.sleep(200) def shipin(name): print('正在和%s视频'%name) time.sleep(300) print("qq主窗口") t1 = threading.Thread(target=talk,args=['刘小燕']) t1.setDaemon(True) #设置线程为守护线程 t1.start() t2 = threading.Thread(target=shipin,args=['蔡明昌']) t2.setDaemon(True) #设置线程为守护线程 t2.start() time.sleep(5) print('结束。。。')
线程锁:
经常出现在数据库操作时,如果多线程同时操作一个数据库操作时,由于线程有快慢先后的区别,原来的数据处理完可能会出错。那么在一个线程来的时候,加上一把锁,只有等这个线程干活结束后,再把锁打开,下一个线程再去操作数据干活,这样数据就不会乱套了
import threading count = 0 lock = threading.Lock()#申请一把锁 def test(): global count print(threading.current_thread()) '''第一个加锁方式,不解锁后出现线程死锁''' # lock.acquire()#加锁 # count+=1 # lock.release()#解锁 '''第二种加锁方式,会自动解锁''' with lock: count+=1 for i in range(3): t = threading.Thread(target=test) t.start()