一、多线程&多进程
对于操作系统来说,一个任务就是一个进程。比如我在电脑上打开视频看电视,再启动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的执行速度实在是太快了,我们感觉就像所有任务都在同时执行一样。这个叫做上下文切换。
· 当需要使用大量网络IO、磁盘IO,不停的读写磁盘等(如存数据库)操作时,最好使用多线程来操作
当需要使用大量消耗CPU性能时,比如排序,复杂计算等,最好使用多进程,因为多进程才能同时使用更多的cpu来进行处理数据
多线程,线程之间的数据是共享的
多进程,每个进程之间的数据是独立的
二、python中通过threading模块来实现多线程
简单启动线程的例子
import threading import time def sayhi(num): #定义每个线程要运行的函数 print("running on number:%s" %num) time.sleep(3) case_result = [] def run_case(casename): case_result.append({casename:'success'}) #多线程运行函数时,函数的返回结果拿不到,需要定义一个list将结果append进去 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=[]来传递参数,当只有一个入参时元组记得加逗号,args=(1,) # 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头像
1 import time 2 3 import requests,hashlib,threading 4 5 def down_load_pics(url): 6 req = requests.get(url) 7 title = hashlib.md5(url.encode()) 8 with open(title.hexdigest()+'.png','wb') as fw: 9 fw.write(req.content) 10 11 url_list = ['https://q4.qlogo.cn/g?b=qq&nk=1345741814&s=140', 12 'https://q4.qlogo.cn/g?b=qq&nk=1134900814&s=140', 13 'https://q4.qlogo.cn/g?b=qq&nk=511402865&s=140' 14 ] 15 16 start_time = time.time() 17 for url in url_list: 18 t = threading.Thread(target=down_load_pics,args=(url,)) 19 t.start() 20 21 while threading.activeCount()!=1:#等待子线程结束 22 pass 23 24 end_time = time.time() 25 print(end_time - start_time)
守护线程:当主线程结束后,守护线程会自动结束。就比如秦始皇死后,所有为他建造皇陵的人全部都要去陪葬。那么秦始皇就是主线程,其他陪葬的都是子线程。
以一个视频聊天为例,关闭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()
三、进程通过multiprocessing模块实现
进程和线程用法和线程相似,都有等待、锁、等操作,一个进程内部可以再启动多个线程
进程原来是使用多个CPU来处理,每个进程之间是独立的,处理的数据是相互独立,而线程处理的数据是共享的,所以多进程的锁意义不大。
import multiprocessing import time import threading lock = multiprocessing.Lock() a =1 def xxx(): pass def mytest(): '''进程中再启20个线程调用xxx方法''' for i in range(20): t = threading.Thread(target=xxx) t.start() time.sleep(3) global a with lock:#加锁并自动解锁 a+=1 print('over') # process = [] #启动进程需要放在main中 if __name__ == '__main__': for i in range(5): p = multiprocessing.Process(target=mytest,name='tyl') p.start() # process.append(p) # print(p.pid)#进程id #主进程等待子线程结束方法1: while len(multiprocessing.active_children())!=0: pass #主进程等待子线程结束方法2: # for x in process: # x.join()