M
is a package that supports spawning processes using an API similar to the threading module. The ultiprocessing
package offers both local and remote concurrency,effectively side-stepping the Global Interpreter Lock by using subprocesses instead of threads. Due to this, the multiprocessing
module allows the programmer to fully leverage multiple processors on a given machine. It runs on both Unix and Windows.multiprocessing
由于GIL的存在,python中的多线程其实并不是真正的多线程,如果想要充分地使用多核CPU的资源,在python中大部分情况需要使用多进程。
multiprocessing包是Python中的多进程管理包。与threading.Thread类似,它可以利用multiprocessing.Process对象来创建一个进程。该进程可以运行在Python程序内部编写的函数。该Process对象与Thread对象的用法相同,也有start(), run(), join()的方法。此外multiprocessing包中也有Lock/Event/Semaphore/Condition类 (这些对象可以像多线程那样,通过参数传递给各个进程),用以同步进程,其用法与threading包中的同名类一致。所以,multiprocessing的很大一部份与threading使用同一套API,只不过换到了多进程的情境。
通过生成进程实例来调用:
from multiprocessing import Process
#import Process
import time
def f(name):
time.sleep(1)
print('hello', name,time.ctime())
if __name__ == '__main__':
p_list=[]
for i in range(3):
p = Process(target=f, args=('alvin',))
#p=multiprocessing.Process(target=f, args=('alvin',))
p_list.append(p)
p.start()
for i in p_list:
p.join()
print('end')
通过继承改写调用:
from multiprocessing import Process
import time
class MyProcess(Process):#自定义函数来继承Process
def __init__(self):
super(MyProcess, self).__init__()
#self.name = name
def run(self):#改写父类方法
time.sleep(1)
print ('hello', self.name,time.ctime())
if __name__ == '__main__':
p_list=[]
for i in range(3):
p = MyProcess()
p.start()
p_list.append(p)
for p in p_list:
p.join()
print('end')
process类:
import time
from multiprocessing import Process
def foo(i):
time.sleep(1)
print (p.is_alive(),i,p.pid)
time.sleep(1)
if __name__ == '__main__':
p_list=[]
for i in range(10):
p = Process(target=foo, args=(i,))
#p.daemon=True
p_list.append(p)
for p in p_list:
p.start()
# for p in p_list:
# p.join()
print('main process end')
构造方法:
Process([group [, target [, name [, args [, kwargs]]]]])
group: 线程组,目前还没有实现,库引用中提示必须是None;
target: 要执行的方法;
name: 进程名;
args/kwargs: 要传入方法的参数。
实例方法:
is_alive():返回进程是否在运行。
join([timeout]):阻塞当前上下文环境的进程程,直到调用此方法的进程终止或到达指定的timeout(可选参数)。
start():进程准备就绪,等待CPU调度
run():strat()调用run方法,如果实例进程时未制定传入target,这star执行t默认run()方法。
terminate():不管任务是否完成,立即停止工作进程
属性:
daemon:和线程的setDeamon功能一样
name:进程名字。
pid:进程号。
进程对列Queue
import queue,time
import multiprocessing
def foo(q):
time.sleep(1)
print("son process",id(q))
q.put(123)
q.put("yuan")
if __name__ == '__main__':
#q=queue.Queue()
q=multiprocessing.Queue()
p=multiprocessing.Process(target=foo,args=(q,))
p.start()
#p.join()
print("main process",id(q))
print(q.get())
print(q.get())
管道
The Pipe()
function returns a pair of connection objects connected by a pipe which by default is duplex (two-way)
from multiprocessing import Process, Pipe
def f(conn):
conn.send([12, {"name":"yuan"}, 'hello'])
response=conn.recv()
print("response",response)
conn.close()
print("q_ID2:",id(conn))
if __name__ == '__main__':
parent_conn, child_conn = Pipe() #双向管道 Pipe一个类()直接产生两个结果,可发可收
print("q_ID1:",id(child_conn))
p = Process(target=f, args=(child_conn,))
p.start()
print(parent_conn.recv()) # prints "[42, None, 'hello']"
parent_conn.send("儿子你好!")
p.join()
Managers
Queue和pipe只是实现了数据交互,并没实现数据共享,即一个进程去更改另一个进程的数据。
Managers 支持类型list
, dict
, Namespace
, Lock
, RLock
, Semaphore
, BoundedSemaphore
, Condition
, Event
, Barrier
, Queue
, Value
and Array
from multiprocessing import Process, Manager
def f(d, l,n):
d[n] = '1' #{0:"1"}
d['2'] = 2 #{0:"1","2":2}
l.append(n) #[0,1,2,3,4, 0,1,2,3,4,5,6,7,8,9]
#print(l)
if __name__ == '__main__':
with Manager() as manager:
d = manager.dict()#{}
l = manager.list(range(5))#[0,1,2,3,4]
p_list = []
for i in range(10):
p = Process(target=f, args=(d,l,i))
p.start()
p_list.append(p)
for res in p_list:
res.join()
print(d)
print(l)
进程同步:如果没有这样会让多个进程同时抢占同一个资源。造成串行等,加把锁一个一个来
from multiprocessing import Process, Lock
def f(l, i):
with l:
print('hello world %s' % i)
# l.acquire() ##不用with可以用这种方式
# print('hello world %s' % i)
# l.release()
if __name__ == '__main__':
lock = Lock()
for num in range(10):
Process(target=f, args=(lock, num)).star
进程池:进程池内部维护一个进程序列,当使用时,则去进程池中获取一个进程,如果进程池序列中没有可供使用的进进程,那么程序就会等待,直到进程池中有可用进程为止。
进程池中有两个方法:apply 同步 apply_asym异步
from multiprocessing import Process,Pool
import time,os
def Foo(i):
time.sleep(1)
print(i)
print("son",os.getpid())
return "HELLO %s"%i
#这里的return值就是回调函数后面的参数
def Bar(arg):
print(arg)
# print("hello")
# print("Bar:",os.getpid())
if __name__ == '__main__':
pool = Pool(5)
print("main pid",os.getpid())
for i in range(100):
#pool.apply(func=Foo, args=(i,)) #同步接口
#pool.apply_async(func=Foo, args=(i,))
#回调函数: 就是某个动作或者函数执行成功后再去执行的函数
#好处:是在主进程中调用,等函数运行完在执行减少内存开销;可减少放置日志文件的次数,直接在回调函数中执行就行
pool.apply_async(func=Foo, args=(i,),callback=Bar)
pool.close()
pool.join() # join与close调用顺序是固定的,close()必须放在join()后面
print('end')
协程
协程,又称微线程,纤程。英文名Coroutine。
优点1: 协程极高的执行效率。因为子程序切换不是线程切换,而是由程序自身控制,因此,没有线程切换的开销,和多线程比,线程数量越多,协程的性能优势就越明显。(没有切换到 消耗)
优点2: 不需要多线程的锁机制,因为只有一个线程,也不存在同时写变量冲突,在协程中控制共享资源不加锁,只需要判断状态就好了,所以执行效率比多线程高很多。(不需要锁的概念)
因为协程是一个线程执行,那怎么利用多核CPU呢?最简单的方法是多进程+协程,既充分利用多核,又充分发挥协程的高效率,可获得极高的性能。
yield的简单实现
import time
import queue
def consumer(name):
print("--->ready to eat baozi...")
while True:
new_baozi = yield
print("[%s] is eating baozi %s" % (name,new_baozi))
#time.sleep(1)
def producer():
r = con.__next__()
r = con2.__next__()
n = 0
while 1:
time.sleep(1)
print("\033[32;1m[producer]\033[0m is making baozi %s and %s" %(n,n+1) )
con.send(n)
con2.send(n+1)
n +=2
if __name__ == '__main__':
con = consumer("c1")
con2 = consumer("c2")
p = producer()
consumer()就不是函数了,就成生成器对象了。到yield就停止,后面代码不执行。send值后完成信息传递,再执行
greenlet是一个用C实现的协程模块,相比与python自带的yield,它可以使你在任意函数之间随意切换,而不需把这个函数先声明为generator
from greenlet import greenlet
def test1():
print(12)
gr2.switch()
print(34)
def test2():
print(56)
gr1.switch()
print(78)
gr1.switch()
gr1 = greenlet(test1)
gr2 = greenlet(test2)
gr2.switch()#切换启动
requests和gevent完成小爬虫
import gevent
import requests,time
# 模块(爬虫)#模块(爬虫)
start=time.time()
def f(url):
print('GET: %s' % url)#url即网址
resp =requests.get(url)#爬
with open('abc.html','w',encoding='utf-8') as f :
resp=f.write()
data = resp.text#打印
print('%d bytes received from %s.' % (len(data), url))
# f('https://www.python.org/')
# f('https://www.yahoo.com/') #串行
# f('https://www.baidu.com/')
# f('https://www.sina.com.cn/')
# f("http://www.xiaohuar.com/hua/")
gevent.joinall([
gevent.spawn(f, 'https://www.python.org/'),
gevent.spawn(f, 'https://www.yahoo.com/'),
gevent.spawn(f, 'https://www.baidu.com/'),
gevent.spawn(f, 'https://www.sina.com.cn/'),
gevent.spawn(f, 'http://www.xiaohuar.com/hua/'),
])
print("cost time:",time.time()-start)