multiprocessing、threading。 线程进程模块
模块 multiprocessing
Queue() 队列 创建a=queue() a.put() a.get()
Process() 进程 p=process(target=fun,args=[val,]) p.start() p.join() 创建方式2,创建类继承
Lock() 锁 lock=lock() lock.acquire() lock.release()
with lock:pass;
Semaphore() 信号量 Semaphore=lock() Semaphore.acquire() Semaphore.release()
JoinableQueue 管道(需要自己加锁)少用,left,right=Pipe() left.put() right.get()
Pool 进程池
p=Pool(5) #创建5个进程池
p.apply_async(func=get_url,args=[url,],callback=call) #为进程池添加任务,call是回调函数,回调参数是fun的return值
p.get()需要等进程完成,返回任务执行之后的值
p.close() # 不能再提交新的任务
p.join() # 等待池中的任务都执行完
p.map(func=wahaha,iterable=range(5)) # iterable接收一个可迭代对象
threading模块
Thread 线程 t=Thread(target=sayhi,args=('egon',)) t.start() 创建方式2,创建类继承
join , t.join() 等待
sAlive(): 返回线程是否活动的。
getName(): 返回线程名。
setName(): 设置线程名。
t.setDaemon(True)守护线程
threading模块提供的一些方法:
# threading.currentThread(): 返回当前的线程变量。
# threading.enumerate(): 返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程。
# threading.activeCount(): 返回正在运行的线程数量,与len(threading.enumerate())有相同的结果。
Lock() 锁 lock=lock() lock.acquire() lock.release()
RLock() 有超过一个资源需要锁的时候,使用递归锁 noodle_lock = fork_lock = RLock() # 面条锁和叉子锁,表示一串钥匙
Semaphore() 信号量 Semaphore管理一个内置的计数器, 计数器不能小于0;当计数器为0时,acquire()将阻塞线程直到其他线程调用release()。
Event()事件 e = Event() e.set()#设置状态为True e.wait(0.5)# 等待0.5,不设置一直等待到e为Ture 你要做一件事情 是有前提的 你就先去处理前提的问题 —— 前提处理好了 把状态设置成True 来控制即将要做的事情可以开始
同进程的一样
线程的一个关键特性是每个线程都是独立运行且状态不可预测。如果程序中的其 他线程需要通过判断某个线程的状态来确定自己下一步的操作,这时线程同步问题就会变得非常棘手。为了解决这些问题,我们需要使用threading库中的Event对象。 对象包含一个可由线程设置的信号标志,它允许线程等待某些事件的发生。在 初始情况下,Event对象中的信号标志被设置为假。如果有线程等待一个Event对象, 而这个Event对象的标志为假,那么这个线程将会被一直阻塞直至该标志为真。一个线程如果将一个Event对象的信号标志设置为真,它将唤醒所有等待这个Event对象的线程。如果一个线程等待一个已经被设置为真的Event对象,那么它将忽略这个事件, 继续执行
event.isSet():返回event的状态值; event.wait():如果 event.isSet()==False将阻塞线程; event.set(): 设置event的状态值为True,所有阻塞池的线程激活进入就绪状态, 等待操作系统调度; event.clear():恢复event的状态值为False。
Condition()条件 使得线程等待,只有满足某条件时,才释放n个线程 con = Condition()
使得线程等待,只有满足某条件时,才释放n个线程
Python提供的Condition对象提供了对复杂线程同步问题的支持。Condition被称为条件变量,除了提供与Lock类似的acquire和release方法外,还提供了wait和notify方法。线程首先acquire一个条件变量,然后判断一些条件。如果条件不满足则wait;如果条件满足,进行一些处理改变条件后,通过notify方法通知其他线程,其他处于wait状态的线程接到通知后会重新判断条件。不断的重复这一过程,从而解决复杂的同步问题。
from threading import Condition,Thread # acquire release # notify -- wait的行为 # 10线程 wait # notify(1) def func(i,con): con.acquire() # 进入锁定池 con.wait() # 等待通知,前后必须要有acquire和release print(i*'*') con.release() # 解锁 con = Condition() #条件变量 for i in range(10): Thread(target=func,args=(i,con)).start() while True: n = int(input('>>>')) con.acquire() # 加锁 con.notify(n) # 通知其他线程,其他处于wait状态的线程接到通知后会重新判断条件,解除wait状态。前后必须要有acquire和release con.release() # 解锁 执行输出: 首先输入2,显示1个*和0个星号,也就是空。 再输入5,分别显示2~6个*号 为什么输入5的时候,出现6个*号? 因为线程池有10个,不等长度的*号 输入2,表示解除2个线程的wait状态,分别表示0,1。 那么5,表示5个。分别表示2,3,4,5,6 从结果上来看,发生数据混乱 con.notify(n) 表示按照要求放开线程 con.notify_all() 表示一次性放开线程 总结: semaphore 允许同一时刻n个线程执行这段代码 event 有一个内部的事件来控制wait的行为,控制的是所有的线程 condition 有一个内部的条件来控制wait的行为,它可以逐个或者分批次的控制线程的走向
总结:
semaphore 允许同一时刻n个线程执行这段代码
event 有一个内部的事件来控制wait的行为,控制的是所有的线程
condition 有一个内部的条件来控制wait的行为,它可以逐个或者分批次的控制线程的走向
queue队列 :使用import queue,用法与进程Queue一样
#后进先出 import queue q = queue.LifoQueue() q.put('first') q.put('second') q.put('third') print(q.get()) #执行输出:third #先进先出 import queue q = queue.Queue() q.put('first') q.put('second') q.put('third') print(q.get()) #执行输出:third #优先级队列 import queue q = queue.PriorityQueue() q.put((1,'a')) q.put((2,'z')) q.put((3,'b')) print(q.get()) #执行输出: #(1, 'a') #等级一样的话,结果是根据ascii码的顺序来排序的
Python标准模块--concurrent.futures
#1 介绍 concurrent.futures模块提供了高度封装的异步调用接口 ThreadPoolExecutor:线程池,提供异步调用 ProcessPoolExecutor: 进程池,提供异步调用 Both implement the same interface, which is defined by the abstract Executor class. #2 基本方法 #submit(fn, *args, **kwargs) 异步提交任务 #map(func, *iterables, timeout=None, chunksize=1) 取代for循环submit的操作 #shutdown(wait=True) 相当于进程池的pool.close()+pool.join()操作 wait=True,等待池内所有任务执行完毕回收完资源后才继续 wait=False,立即返回,并不会等待池内的任务执行完毕 但不管wait参数为何值,整个程序都会等到所有任务执行完毕 submit和map必须在shutdown之前 #result(timeout=None) 取得结果 #add_done_callback(fn) 回调函数
#介绍 The ProcessPoolExecutor class is an Executor subclass that uses a pool of processes to execute calls asynchronously. ProcessPoolExecutor uses the multiprocessing module, which allows it to side-step the Global Interpreter Lock but also means that only picklable objects can be executed and returned. class concurrent.futures.ProcessPoolExecutor(max_workers=None, mp_context=None) An Executor subclass that executes calls asynchronously using a pool of at most max_workers processes. If max_workers is None or not given, it will default to the number of processors on the machine. If max_workers is lower or equal to 0, then a ValueError will be raised. #用法 from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor import os,time,random def task(n): print('%s is runing' %os.getpid()) time.sleep(random.randint(1,3)) return n**2 if __name__ == '__main__': executor=ProcessPoolExecutor(max_workers=3) futures=[] for i in range(11): future=executor.submit(task,i) futures.append(future) executor.shutdown(True) print('+++>') for future in futures: print(future.result())
#介绍 ThreadPoolExecutor is an Executor subclass that uses a pool of threads to execute calls asynchronously. class concurrent.futures.ThreadPoolExecutor(max_workers=None, thread_name_prefix='') An Executor subclass that uses a pool of at most max_workers threads to execute calls asynchronously. Changed in version 3.5: If max_workers is None or not given, it will default to the number of processors on the machine, multiplied by 5, assuming that ThreadPoolExecutor is often used to overlap I/O instead of CPU work and the number of workers should be higher than the number of workers for ProcessPoolExecutor. New in version 3.6: The thread_name_prefix argument was added to allow users to control the threading.Thread names for worker threads created by the pool for easier debugging. #用法 与ProcessPoolExecutor相同
from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor import os,time,random def task(n): print('%s is runing' %os.getpid()) time.sleep(random.randint(1,3)) return n**2 if __name__ == '__main__': executor=ThreadPoolExecutor(max_workers=3) # for i in range(11): # future=executor.submit(task,i) executor.map(task,range(1,12)) #map取代了for+submit
from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor from multiprocessing import Pool import requests import json import os def get_page(url): print('<进程%s> get %s' %(os.getpid(),url)) respone=requests.get(url) if respone.status_code == 200: return {'url':url,'text':respone.text} def parse_page(res): res=res.result() print('<进程%s> parse %s' %(os.getpid(),res['url'])) parse_res='url:<%s> size:[%s]\n' %(res['url'],len(res['text'])) with open('db.txt','a') as f: f.write(parse_res) if __name__ == '__main__': urls=[ 'https://www.baidu.com', 'https://www.python.org', 'https://www.openstack.org', 'https://help.github.com/', 'http://www.sina.com.cn/' ] # p=Pool(3) # for url in urls: # p.apply_async(get_page,args=(url,),callback=pasrse_page) # p.close() # p.join() p=ProcessPoolExecutor(3) for url in urls: p.submit(get_page,url).add_done_callback(parse_page) #parse_page拿到的是一个future对象obj,需要用obj.result()拿到结果
当内存不需要共享,且高计算的时候 用进程
当内存需要共享,且高IO的时候 用线程
当并发很大的时候
多进程 : 多个任务 —— 进程池 :cpu个数、cpu个数+1
多线程 :多个任务 —— 线程池 :cpu个数*5
4核CPU,可以开4~5个进程,开20条线程/进程。最终可以开80~100个任务
最美的程序,不是单一使用。而是复合使用
比如开多个进程,每个进程,开多个线程。
当后面学到协程,并发(qps)能达到5万
锁的概念,面试,会经常问到,还有队列
真正工作过程中,锁会用到,其他的,很少用。
还有进程池/线程池也会用到
semaphore 在一开始固定一个线程的流量
condition 通过一个信号控制线程的流量
event 通过一个信号控制所有线程
timer 定时器
队列 线程数据安全
线程池
能够在多线程的基础上进一步节省内存和时间开销

浙公网安备 33010602011771号