Twenty-seven 进程

 

1、进程:

  几个概念:父进程、子进程、主进程

import os
import time
from multiprocessing import Process

def func():
    time.sleep(1)
    print('hello',os.getpid(),os.getppid())

def func2():
    print('world')

if __name__ == '__main__':
    for i in range(3):
        p = Process(target=func)
        p.start()   # 开启了一个新的进程,在这个新的进程里执行func中的代码
    for i in range(2):
        p = Process(target=func2)
        p.start()   # 开启了一个新的进程,在这个新的进程里执行func中的代码
    time.sleep(1)
    print('hello2',os.getpid(),os.getppid())
import time
from multiprocessing import Process
def func(num,name):
    print(num,'hello',name)

if __name__ == '__main__':
    for i in range(10):
        p = Process(target=func,args=(i,'yuan',))    # 传参
        p.start()    # 非阻塞的
    print('主进程')

2、join方法:阻塞,直到子进程执行结束,这样能保证所有的子进程都能执行完毕

from multiprocessing import Process
import time
def send_mail(n):
    time.sleep(0.5)
    print('发送邮件%s'%n)

if __name__ == '__main__':
    l = []
    for i in range(10):
        p = Process(target=send_mail,args=(i,))
        l.append(p)
        p.start()
    for p in l:
        p.join()
    print('所有的邮件都已经发出了')

3、进程之间数据隔离,进程之间的数据是隔离独立的

4、开启进程的第二种方式,懂一种就行了

import os
from multiprocessing import Process

class MyProcess(Process):
    def __init__(self,arg):
        super().__init__()
        self.arg = arg
    def run(self):
        print(os.getpid(),self.arg)

if __name__ == '__main__':
    p = MyProcess(123)
    p.start()
    p.join()
    print('',os.getpid())

5、进程对象的其它方法:判断进程是否活着,消灭进程

import time
from multiprocessing import Process

class MyProcess(Process):
    def __init__(self,a,b):
        super().__init__()
        self.a = a
        self.b = b
    def run(self):
        print('start')
        time.sleep(10)
        print('end',self.a,self.b)

if __name__ == '__main__':
    p = MyProcess(1,2)
    p.start()
    print(p.is_alive())
    time.sleep(1)
    print(p.name, p.pid)
    p.terminate()   # 非阻塞,消灭进程,由于是非阻塞的,可能马上查看的时候还没有分配到资源去删除
    print(p.is_alive())  # 查看是否还活着
    time.sleep(0.1)
    print(p.is_alive())

6、守护进程:知道进程是否有在正常运行,保证程序安全运行等

# 1.守护进程 会随着主进程代码的结束而结束
# 2.守护进程不会守护除了主进程代码之外的其他子进程
import time
from multiprocessing import Process
def eye():
    while True:
        print('告诉server端我很好')
        time.sleep(1)

def process2():
    print('进程2要做的事情')
    time.sleep(8)
    print('进程2要做的事情完成')

def main():
    print('我想做的事情')
    time.sleep(5)
    print('done')

if __name__ == '__main__':
    p = Process(target=eye)
    p2 = Process(target=process2)
    p.daemon = True   # p进程设置成 守护进程
    p.start()
    p2.start()
    main()
    p2.join()   # p2结束之后这句代码才会结束,延缓结束达到在全部进程结束再结束

7、socket并发server端:原理,来一个客户端就新开一个进程

# 使用多进程 实现socket tcp协议 server端的并发
import socket
from multiprocessing import Process

def func(conn):
    while True:
        conn.send(b'hello')

if __name__ == '__main__':
    sk = socket.socket()
    sk.bind(('127.0.0.1',9000))
    sk.listen()
    while True:
        conn,addr = sk.accept()
        p = Process(target=func,args = (conn,))
        p.start()

8、抢票例子,由于每个购票者都是打开文件并写入新的票数,如果同时打开就会造成票数多卖的情况,解决方法如下

import json
import time
from multiprocessing import Lock
from multiprocessing import Process
def search(name):
    with open('ticket') as f:
        ticket_count = json.load(f)
    if ticket_count['count'] >=1:
        print('%s : 有余票%s张'%(name,ticket_count['count']))
    else:
        print('%s : 没票了'%name)

def buy(name):
    with open('ticket') as f:
        ticket_count = json.load(f)
    time.sleep(0.2)
    if ticket_count['count'] >=1:
        print('有余票%s张'%ticket_count['count'])
        ticket_count['count'] -= 1
        print('%s买到票了'%name)
    else:
        print('%s没买到票' % name)
    time.sleep(0.2)
    with open('ticket','w') as f:
        json.dump(ticket_count,f)

def opt(lock,name):
    search(name)
    lock.acquire()   # 拿走钥匙
    buy(name)
    lock.release()   # 归还钥匙

if __name__ == '__main__':
    lock = Lock()   # 锁  互斥锁
    for i in range(10):
        p = Process(target=opt,args = (lock,'alex'+str(i),))
        p.start()
# 多个进程 抢占同一个数据资源 会造成 数据不安全
# 我们必须要牺牲效率来保证数据的安全性

9、队列,维持数据,先进先出只能一个使用,就是抢票的升级简化版

# 例子
import queue
q = queue.Queue()
print(q)
q.put(1)
q.put(2)
q.put(3)
print(q.get())
print(q.get())
print(q.get())
# 实际应用
from multiprocessing import Queue,Process
def son(q):
    msg = q.get()
    print(msg)

if __name__ == '__main__':
    q = Queue()    # pickle + socket + Lock  进程之间数据安全的数据类型
    pro = Process(target=son,args = (q,))
    pro.start()
    q.put('hello')
# 进程之间的通信
# IPC Iter Process Communication
# IPC :管道Pipe(没有锁,数据不安全的),管道 + 锁 == 队列
# 第三方工具(消息中间件) :memcache、redis、kafka、rabbitmq
# 队列的用法 + 模型

10、生产者消费者模型(非常重要):生成和消费之间的传递数据是需要一个盘子,主要用来提高CPU的使用率提高代码速度

import time
from multiprocessing import Queue,Process
def consumer(name,q):
    while True:
        food = q.get()   # get 会阻塞 直到队列里有一个数据,就取出来
        if not food:break
        time.sleep(1)
        print('%s吃了一个%s'%(name,food))

def producer(q,food_name):
    for i in range(20):
        time.sleep(0.1)
        food = '%s%s'%(food_name,i)
        print('制造了%s' % food)
        q.put(food)    # put 如果队列满了 就在这里阻塞 直到队列里有空的位置

if __name__ == '__main__':
    q = Queue(5)  # qsize 这个队列的容量,否则生产速度过快会导致内存拥挤死机
    p1 = Process(target=consumer,args = ('alex',q))
    p3 = Process(target=consumer,args = ('wusir',q))
    p2 = Process(target=producer,args = (q,'泔水'))
    p1.start()
    p2.start()
    p3.start()
    p2.join()   # 确认所有的生产者把数据都生产完毕了
    q.put(None)
    q.put(None)
    # ['泔水0'...'泔水9',None,None]
# 吃的人是消费数据的
# 制造吃的的人是生产数据的
# 生产的快 消费的慢 供大于求
    # 增加消费者
# 生产的慢 消费的快 供不应求
    # 增加生产者

11、数据共享Manager提供很多数据共享的机制,但是对于一些基础数据类型来说,数据是不安全的,可以加锁(少用)

from multiprocessing import Manager,Process,Lock
def work(d,lock):
    # lock.acquire()
    # d['count']-=1
    # lock.release()
    with lock:           # 上下文管理
        d['count'] -= 1  # 这句话之前的上文 lock.acquire(),下文是lock.release()

if __name__ == '__main__':
    lock=Lock()
    m = Manager()
    dic=m.dict({'count':100,'key':99})
    p_l=[]
    for i in range(100):
        p=Process(target=work,args=(dic,lock))
        p_l.append(p)
        p.start()
    for p in p_l:
        p.join()
    print(dic)

12、进程池:进程不可能无限新建,因为进程含(创建、分配、结束等)会极大占用CPU,使用当有超过CPU个数的任务或数据总是要利用到数据共享应该用进程池来控制数量(CPU+1个)

import os
import time
from multiprocessing import Pool

def func(i):
    time.sleep(0.1)
    print(i,os.getpid())

if __name__ == '__main__':
    p = Pool(4)   # cpu个数 + 1/cpu的个数
    for i in range(10):
        p.apply_async(func,args=(i,))   # async异步的提交任务
    p.close() # 关闭池子,不是要回收池子中的进程,而是阻止继续向池子中提交任务
    p.join()  # 阻塞,直到池子中的任务都执行完毕
# 结果:总是在4个进程以内重复使用
0 4624
1 7640
2 5360
3 1268
4 4624
5 7640
6 5360
7 1268
8 4624
9 7640
# 进程池于多进程的性能测试
import os
import time
from multiprocessing import Pool,Process

def func(i):
    print(i,os.getpid())

if __name__ == '__main__':
    start = time.time()
    p_lst = []
    for i in range(100):
        p = Process(target=func,args = (i,))
        p.start()
        p_lst.append(p)
    for p in p_lst:
        p.join()
    end = time.time()
    pro_time = end-start
    start = time.time()
    p = Pool(4)
    for i in range(100):
        p.apply_async(func,args=(i,))   # async异步的提交任务
    p.close() # 关闭池子,不是要回收池子中的进程,而是阻止继续向池子中提交任务
    p.join()  # 阻塞,直到池子中的任务都执行完毕
    end = time.time()
    pool_time = end - start
    print(pro_time,pool_time)

13、数据池的其它机制:可以利用队列进行进程之间的通信,即子程序往队列放,主程序往队列拿

#普通
import os
import time
from multiprocessing import Pool

def func(i):
    time.sleep(1)
    print(i,os.getpid())
    return i**i

if __name__ == '__main__':
    p = Pool(4)   # cpu个数 + 1/cpu的个数
    ret_lst = []
    for i in range(10):
        ret = p.apply_async(func,args=(i,))   # async异步的提交任务
        ret_lst.append(ret)
    p.close() # 关闭池子,不是要回收池子中的进程,而是阻止继续向池子中提交任务
    p.join()  # 阻塞,直到池子中的任务都执行完毕
    for ret in ret_lst:
        print(ret.get())
# map版
import os
import time
from multiprocessing import Pool

def func(i):
    time.sleep(0.1)
    print(i,os.getpid())
    return i ** i

if __name__ == '__main__':
    p = Pool(4)   # cpu个数 + 1/cpu的个数
    ret = p.map(func,range(10))
    for r in ret:
        print(r)

14、进程中的回调函数,执行完毕后返回值给主程序,这样就能发出多个子程序时,将最快返回的子程序值进行数据处理,提高效率

import time
import random
from multiprocessing import Process,Pool
def get(i):    # 进程池的子进程执行的
    time.sleep(random.random())
    print('从网页获取一个网页的内容', i)
    return i,'网页的内容'*i

def call_back(content):   # 主进程执行的
    print(content)

if __name__ == '__main__':
    p = Pool(5)
    ret_l = []
    for i in range(10):
        p.apply_async(get,args=(i,),callback=call_back)
    p.close()
    p.join()
# 将n个任务交给n个进程去执行
# 每一个进程在执行完毕之后会有一个返回值,这个返回值会直接交给callback参数指定的那个函数去进行处理
# 这样的话 所有的进程 哪一个执行的最快,哪一个就可以先进性统计工作
# 能在最短的时间内得到结果

 15、信号量

import time
from threading import Semaphore,Thread

def func(name,sem):
    sem.acquire()
    print(name,'start')
    time.sleep(1)
    print(name,'stop')
    sem.release()

sem = Semaphore(5)
for i in range(20):
    Thread(target=func,args=(i,sem)).start()

  信号量和池的区别:

    进程池:有1000个任务,一个进程池中有5个进程,所有的1000个任务多次利用这5个进程来完成任务

    信号量:有1000个任务,有1000个进程/线程,所有的1000个任务由信号量的控制,只能5个5个的执行

posted @ 2019-01-25 22:33  pythonernoob  阅读(127)  评论(0)    收藏  举报