python学习笔记(二十一)-- 进程、线程
1、进程VS线程
进程
对于操作系统来说,一个任务就是一个进程。
进程一个程序的执行实例就是一个进程,每一个进程提供执行程序所需的所有资源。
一个进程包含一个主线程,0或者多个子线程。
线程
线程是执行的最小单位。
线程不分配自己的资源,所有线程共享进程的资源,线程之间互不影响。
线程的种类
主线程
子线程、
守护线程
2、多线程
Python里面多线程不能利用多个CPU,它只能在一个CPU上运行。
Python多线程使用threading模块实现
import threading #线程模块 import time def run(url): time.sleep(2) print(url) print('running...') def eat(): print('吃饭') start_time = time.time()#程序开始运行时的时间戳 threads = [] for i in range(10): # 实例化一个线程,run是函数名 t = threading.Thread(target=run,args=('http://www.baidu.com',)) # 一个参数一定加个逗号 t.start()#启动这个线程 threads.append(t)#把所有子进程加进threads列表 # for thread in threads: # thread.join()#使用join,主线程就会等待子线程执行结束再往下执行 #使用上面join的方式或者while循环的方式 # 让主线程等待子线程执行完成再执行下面的程序 while threading.active_count() != 1: #当线程数为1,才结束循环 pass print(threading.active_count()) #当前线程数 end_time = time.time()#程序结束时的时间戳 print('运行时间',end_time-start_time) #这个时间是主线程执行完代码的时候,不包含子线程执行时间
多线程下载图片
import time ,requests, threading from hashlib import md5 urls = [ 'http://www.178linux.com/wp-content/uploads/2018/02/5.jpg ', 'http://img1.imgtn.bdimg.com/it/u=1139158180,2224775217&fm=11&gp=0.jpg ', 'http://www.linuxidc.com/upload/2019_04/19041915053582.png ', 'http://www.veryxue.com/file/upload/201905/09/201804031578.jpg ', 'http://5b0988e595225.cdn.sohucs.com/images/20171209/8e81dcb041a9425c823daf6b6053e03b.jpg ' ] result = [] def down_load_picture(url): r = requests.get(url) file_name = md5(r.content).hexdigest() with open(file_name+'.jpg','wb') as fw: fw.write(r.content) result.append(file_name) print('[%s]下载完成'%file_name) start_time = time.time() for url in urls: t = threading.Thread(target=down_load_picture,args=(url,)) t.start() print(result)#在这里打印不出来,因为还没有下载完成就走到这里了 while threading.active_count() != 1: pass end_time = time.time() run_time = end_time - start_time print('下载时间是%s'%run_time)
3、守护线程
守护线程通过setDaemon(True)设置,守护线程只守护主线程。
当主线程运行完成,守护线程自动结束。
import threading,time #守护线程 只能守护主线程 print('QQ上线啦!') def conversation(): print('聊天窗口,每天上班先聊2小时,正在聊...') time.sleep(20) print('聊完了') for i in range(10): t = threading.Thread(target=conversation) t.setDaemon(True)#设置成守护线程,主线程运行完成,守护线程自动结束 t.start() time.sleep(1) print('领导来了,退出QQ')
4、线程锁
多个线程操作同一个数据时,有可能会出问题,这个时候就需要把操作的数据加锁,操作完成后再解锁,这样同一时间就只有一个线程可以操作这块数据。
import threading lock = threading.Lock()#实例化一把锁 sum = 0 def add(): global sum with lock: #加锁 sum += 1 #也可以这样加锁 # lock.acquire()#加锁 # sum += 1 # lock.release()#解锁 for i in range(10): t = threading.Thread(target=add) t.start() while threading.active_count()!=1: pass print(sum)
5、多进程
Python的多线程是不能利用多核CPU的,想要利用多核CPU,需要开启多进程。
import multiprocessing,threading,random def multi_test(): for i in range(5): t = threading.Thread(target=t_test)#开启线程,从随机数能看出开启了不同线程 t.start() def t_test(): print("这是一个线程%s"%random.randint(1,10)) if __name__ == '__main__': for i in range(10): #循环10个进程,并调用循环开启线程的函数 m = multiprocessing.Process(target=multi_test) m.start() while len(multiprocessing.active_children())!=1: pass print('结束')
6、线程池
可以使用线程池来批量启动线程,它可以自己管理线程数,最多启动设置的最大线程数,需要多少开启多少。如果线程池中线程数已满,这时有新的请求则会等待运行的线程结束后再执行新的线程。
import threadpool import time urls = [ 'http://www.178linux.com/wp-content/uploads/2018/02/5.jpg ', 'http://img1.imgtn.bdimg.com/it/u=1139158180,2224775217&fm=11&gp=0.jpg ', 'http://www.linuxidc.com/upload/2019_04/19041915053582.png ', 'http://www.veryxue.com/file/upload/201905/09/201804031578.jpg ', 'http://5b0988e595225.cdn.sohucs.com/images/20171209/8e81dcb041a9425c823daf6b6053e03b.jpg ' ] def test(url): time.sleep(2) print('test...',url) pool = threadpool.ThreadPool(2)#指定线程池大小,这是最大值,用多少会启动多少 reqs = threadpool.makeRequests(test,urls)#第一个参数是函数名,第二个参数是所有的数据 [pool.putRequest(r) for r in reqs] # for r in reqs: # pool.putRequest(r) pool.wait()#等待子线程执行结束 print('over!')
7、进程池
批量开启进程,自己对进程数进行管理,同线程池一样。
import multiprocessing import os def worker(msg): print("%s开始执行,进程号为%d" % (msg, os.getpid())) if __name__ == '__main__': po = multiprocessing.Pool(3) # 定义一个进程池,最大进程数3 for i in range(0, 10): # Pool().apply_async(要调用的目标,(传递给目标的参数元祖,)) # 每次循环将会用空闲出来的子进程去调用目标 po.apply_async(func=worker, args=(i,)) # 第一个func参数指定运行的函数,第二个args是参数,没有参数可以不写 print("----start----") po.close() # 关闭进程池,关闭后po不再接收新的请求 po.join() # 等待po中所有子进程执行完成,必须放在close语句之后 print("-----end-----")