#进程的调用方式和线程差不多,基本一样,但是进程用到了多核,是真正意义上的并发,下面的例子可以看出这个现象
from multiprocessing 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_list.append(p)
p.start()
for i in p_list:
i.join()
print('end')
import multiprocessing,time
def mul(m):
mul = 1
for i in range(1,m):
mul*=i
print(mul)
def mul1(m):
mul = 1
for i in range(1,m):
mul*=i
print(mul)
if __name__ == '__main__':
t = time.time()
p1 = multiprocessing.Process(target=mul,args=(100000,))
p2 = multiprocessing.Process(target=mul1, args=(100000,))
p1.start()
p2.start()
p1.join()
p2.join()
print('end----------',time.time()-t)#27.518165826797485
t = time.time()
mul1(100000)
mul(100000)
print('end----------', time.time() - t)#54.77262306213379
import threading
tm = time.time()
t1 = threading.Thread(target=mul,args=(100000,))
t2 = threading.Thread(target=mul1, args=(100000,))
l = []
l.append(t1)
l.append(t2)
for t in l:
t.start()
for t in l:
t.join()
print('end----------', time.time() - tm)#54.54677987098694#
from multiprocessing import Process
import time
class MyProcess(Process):
def __init__(self,name):
super(MyProcess,self).__init__()
self.name = name
def run(self):
time.sleep(1)
print('helle %s'%self.name,time.ctime())
if __name__ == '__main__':
l = []
for i in range(5):
l.append(MyProcess('%dalex'%i))
for p in l:
p.start()
for p in l:
p.join()
print('ending----------')
构造方法:
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:进程号。
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')
进程通信
进程队列Queue
import queue,multiprocessing,time
def foo(q):
time.sleep(1)
print('son.process',id(q))#son.process 52005168 #son.process 51108208两个子进程的ID并不一样
q.put(123)
q.put('hello')
if __name__ == '__main__':
q = multiprocessing.Queue()
p1 = multiprocessing.Process(target=foo,args=(q,))
p2 = multiprocessing.Process(target=foo, args=(q,))
p1.start()
p2.start()
p1.join()
p2.join()
print('main process',id(q))#main process 46033040
print(q.get())
print(q.get())
管道
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() #双向管道
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只是实现了数据交互,并没实现数据共享,即一个进程去更改另一个进程的数据。
from multiprocessing import Process,Manager
def foo(d,l,n):
d[n] = '1'
d['2'] = 2
d[3] = '3'
l.append(n)
print('son process',id(d),id(l))
if __name__ == '__main__':
with Manager() as manager:
d = manager.dict()
l = manager.list(range(3))
print('main process',id(d),id(l))#main process 47908880 47212976
p_list = []
for i in range(4):
p = Process(target=foo, args=(d, l, 1))
p.start()#son process 44165808 44153072 son process 58059440 58046704
#son process 54848176 54835440 son process 46656176 46643440
#四个子进程公享一个列表和字典
p_list.append(p)
for res in p_list:
res.join()
print(d)#{1: '1', '2': 2, 3: '3'}
print(l)#[0, 1, 2, 1, 1, 1, 1]
进程同步
from multiprocessing import Process, Lock
import time
def f(l, i):
#l.acquire()
time.sleep(1)
print('hello world %s' % i)
#l.release()
if __name__ == '__main__':
lock = Lock()
for num in range(10):
Process(target=f, args=(lock, num)).start()
#如果实在cmd终端上运行,不加锁的话打印时可能出现10个进程有的进程打印的东西在同一行上,原因是这十个进程公用屏幕的资源,哪个进程
#先抢到,哪个进程先打印,如果一样快的会就会共同使用屏幕资源,这是加锁的话就是屏幕这个资源同一时间只能运行一个进程,相当于串行
进程池
from multiprocessing import Process,Pool
import time,os
def Foo(i):
time.sleep(5)
print(i,"son",os.getpid())#0 son 8956 1 son 7736 子进程id是一直变化的
return "HELLO %s"%i
def Bar(arg):#这个arg就是之前函数的的返回值,就是 "HELLO %s"%i,这个是必需要填的
#print(arg)
# print("hello")
print(arg,"Bar:",os.getpid())#HELLO 0 Bar: 972 HELLO 1 Bar: 972 id972始终是固定的
if __name__ == '__main__':
pool = Pool(5)#这就是进程池,进程池最大进程数是5,如果不写就默认按着电脑的核数与运行
print("main pid",os.getpid())#main pid 972 主进程ID是972
for i in range(100):
#pool.apply(func=Foo, args=(i,)) #同步接口
#pool.apply_async(func=Foo, args=(i,))
#回调函数: 就是某个动作或者函数执行成功后再去执行的函数,foo函数执行完之后会立即执行Bar函数,bar函数是在主进程下运行
#如果把Bar函数放到foo下,那么这个逻辑是在子进程下运行
pool.apply_async(func=Foo, args=(i,),callback=Bar)
pool.close()
pool.join() # join与close调用顺序是固定的,这个是规定的格式
print('end')
#举例100块砖,一个人最多搬一块儿,怎么搬
#如果找100个人,那么资源消耗太多,如果1个人去搬,那么时间太长
#我们开一个池子,就是进程池,就是限定一下里面最大的进程数设置为5,意思就是最大有5个人过来搬
#如果我们的电脑是4核,那么我们的进程运行有快有慢,这时先运行完的就结束了,此时进程池空出一个位置,这时候就能再进来一个进程
#为什么还是5个5个的出,是因为电脑运行很快,看不出来明显差距
协程
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__()#这个相当于consumer函数执行了一次
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)#这个地方相当于把包子给到了con即consumer的new_baozi拿到了这个值,并且继续往下走
con2.send(n+1)
n +=2
if __name__ == '__main__':
con = consumer("c1")#这个不是执行函数,只是创建了两个生成器,没有__next__方法,consumer函数是不执行的
con2 = consumer("c2")
producer()
#上面这个例子已经是一个协程的提现了
Greenlet
greenlet是一个用C实现的协程模块,相比与python自带的yield,它可以使你在任意函数之间随意切换,而不需把这个函数先声明为generator
from greenlet import greenlet
def test1():
print(12)
gr2.switch()
print(13)
gr2.switch()
print(14)
def test2():
print(12)
gr1.switch()
print(13)
gr1.switch()
print(14)#这个没有被执行,因为test1函数打印14的后面没有gr2.switch()
gr2 = greenlet(test2)
gr1 = greenlet(test1)
gr1.switch()#switch就是切换执行函数
Gevent
import gevent
import requests,time
start=time.time()
def f(url):
print('GET: %s' % url)
resp =requests.get(url)
data = resp.text
end = time.time()
print('%d bytes received from %s.' % (len(data), url),end-start)
# 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/'),#4.989989757537842
# gevent.spawn(f, 'https://www.yahoo.com/'),#13.485978603363037
# gevent.spawn(f, 'https://www.baidu.com/'),#13.558139324188232
# gevent.spawn(f, 'https://www.sina.com.cn/')#13.657403945922852,最后一共用时13.65左右
# ])
f('https://www.python.org/')
f('https://www.yahoo.com/')
f('https://baidu.com/')
f('https://www.sina.com.cn/')#13.742876529693604这个最终用时13.74
#线程,进程争夺资源是无法控制的,哪个先拿到资源哪个先用
#协程,协作式:--------非抢占式的程序
# yield(协程的关键词)
#协程主要解决的也是IO操作的,协程本质上就是一个线程,所以中间的转换消耗就没有了
#优势:没有切换的消耗,没有锁的概念了
#有问题,能用多核吗?可以采用多进程+协程,一个很好的解决并发的方案
本文通过多个示例对比了Python中进程与线程的区别及应用,包括进程的基本使用方法、进程间通信方式如队列和管道等,以及进程池的使用场景。同时介绍了协程的概念及其优势。
3万+

被折叠的 条评论
为什么被折叠?



