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()事件   = 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。
even事件

 

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())
ProcessPoolExecutor

 

#介绍
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相同
ThreadPoolExecutor
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
map的用法
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 定时器
队列 线程数据安全

线程池
能够在多线程的基础上进一步节省内存和时间开销

posted @ 2019-01-21 14:33  小马_81  阅读(271)  评论(0)    收藏  举报