王者荣耀Cpython解释器支持的历程同线程

  来深圳工作已经抢半单月了,我同学已我楼下,我来的那天,她不怕几乎每天收工回来都会玩“王者荣耀”,她说不行好游戏,很多口都于戏。

一致、理论有

本着之,我反对,觉得不就是打打杀杀的呢,有啊好玩的。

一如既往 什么是过程

    进程:正在拓展的一个过程还是说一个任务。而背负实施任务虽然是cpu。

    举例(单核+多道,实现多单过程的出现执行):

   
egon在一个时空段外发众多职责要举行:python备课的天职,写书之天职,交女朋友的任务,王者荣耀上分的职责,  

   
但egon同一时刻只能开一个职责(cpu同一时间只会干一个生存),如何才能够打来多单任务并发执行之功用?

   
egon备一会课,再夺和李杰的女对象聊聊天,再失去于一会晤王者荣耀….这就是确保了每个任务还当进展中.

 

亚 进程以及程序的别

程序仅仅只是一堆放代码而已,而经过指的凡程序的周转过程。

举例:

想像一各类产生手腕好厨艺的微处理器科学家egon正在为他的丫头元昊烘制生日蛋糕。

外发做生日蛋糕的食谱,

厨里拥有得的原材料:面粉、鸡蛋、韭菜,蒜泥等。

每当是比喻中:

    做蛋糕的菜谱就是先后(即用相当形式描述的算法)

    计算机科学家就电脑(cpu)

    而召开蛋糕的各种原料就是是输入数据

 
 进程就是炊事员阅读食谱、取来各种原料及烘制蛋糕等一样密密麻麻动作的总和

 

本若计算机科学家egon的儿子alex哭着跑了上,说:XXXXXXXXXXXXXX。

科学家egon想了纪念,处理儿子alex蛰伤的天职比较给女儿元昊举行蛋糕的任务更要紧,于是

电脑科学家就记下下客仍在食谱做到哪里了(保存进程的目前状态),然后以出同样按急救手册,按照中的指示处理蛰伤。这里,我们见到处理机从一个经过(做蛋糕)切换至另外一个大优先级的历程(实施医疗急救),每个过程具有各自的次第(食谱和急救手册)。当蜜蜂蛰伤处理了事后,这号电脑科学家同时返做蛋糕,从外
去时的那么同样步累举行下去。

用强调的是:同一个程序执行两糟,那呢是简单个经过,比如打开暴风影音,虽然还是和一个软件,但是一个得以播放苍井空,一个得以播放饭岛好。

 

  前几乎上,她来我楼上蹭网,边打边说,这游戏大有趣的,你啊得生一个,我当下都不是殊抗拒这款打了,于是就下了一个来玩,

其三 并作与相

不论是并行还是出现,在用户看来都是’同时’运行的,不管是过程或线程,都只是一个任务而已,真是干活的凡cpu,cpu来举行这些任务,而一个cpu同一时刻只能实行一个职责

      一
并作:是伪并行,即看起是同时运转。单个cpu+多鸣技术就可以兑现产出,(并行也属于并发)

     二 并行:同时运转,只有具有多只cpu才会兑现相互之间

       
 单核下,可以运用基本上道技术,多只审批,每个核也还可以动用基本上道技术(大多鸣技术是对单核而言的

       
 有四个审批,六个任务,这样同一时间有四单任务让实施,假而分别叫分配受了cpu1,cpu2,cpu3,cpu4,

       
 一旦任务1遇I/O就被迫暂停执行,此时任务5不怕拿到cpu1之年华片去实践,这即是仅核下的多道技术

       
 而设任务1之I/O结束了,操作系统会重新调用它(需知进程的调度、分配给哪个cpu运行,由操作系统说了算),可能吃分配为四单cpu中的肆意一个去执行

  王者荣耀 1

有着现代计算机经常会面于同一时间做过多宗事,一个用户的PC(无论是单cpu还是多cpu),都可以而且运转多只任务(一个职责可以知道吧一个经过)。

    启动一个过程来杀毒(360软件)

    启动一个经过来拘禁电影(暴风影音)

    启动一个进程来拉(腾讯QQ)

怀有的这些经过都用让管理,于是一个支持多进程的多道程序系统是至关重要的

基本上道技术概念回顾:内存中同时存入多道(多只)程序,cpu于一个进程快速切换到另外一个,使每个过程各自运行几十还是几百毫秒,这样,虽然在某个一个一晃,一个cpu只能执行一个任务,但在1秒内,cpu却可运作多个经过,这便叫人产生了互动的错觉,即伪并发,以这个来分别多处理器操作系统的真的硬件并行(多个cpu共享同一个物理内存)

哎,觉得还不行好玩的,当天夜即使玩到少碰多钟,此后之几龙均是如此,包括今,手机没有电了才告一段落下来。

季 同步跟异步

手拉手执行:一个历程在执行有任务时,另外一个过程必须等待其实践了,才会继续执行
异步执行:一个过程在实行有任务时,另外一个经过无需等待其履行完毕,就可以继续执行,当有信息返回时,系统会通报后者进行处理,这样可提高实践效率

举个例子,打电话时即同通信,发短息时就是异步通信。

 

五 进程的创始(了解)

  但凡是硬件,都亟待有操作系统去管理,只要出操作系统,就生过程的定义,就得发创造进程的方式,一些操作系统只为一个应用程序设计,比如微波炉中之控制器,一旦启动微波炉,所有的经过都早就在。

  而对此通用系统(跑多应用程序),需要有系统运转过程中创造或者注销进程的力,主要分为4遭到形式创建新的长河

  1.
体系初始化(查看进程linux中因故ps命令,windows中之所以任务管理器,前台进程负责与用户交互,后台运行的进程与用户无关,运行在后台并且仅以得常才提醒的历程,称为守护进程,如电子邮件、web页面、新闻、打印)

  2.
一个历程在运行过程中拉开了分段进程(如nginx开启多进程,os.fork,subprocess.Popen等)

  3. 用户之交互式请求,而创造一个新过程(如用户双击暴风影音)

  4. 一个批判处理作业的初始化(只以大型机的批判处理系统受到应用)

  

  无论哪一样种植,新过程的创办都是由一个已经在的过程执行了一个用于创造过程的体系调用而创办的:

  1.
在UNIX中该网调用是:fork,fork会创建一个同大进程同模型一样的副本,二者有同的存储映像、同样的条件字符串和同一的开辟文件(在shell解释器进程被,执行一个限令就会创一个子经过)

  2.
每当windows中该网调用是:CreateProcess,CreateProcess既处理进程的开创,也承担将对的次装入新历程。

 

  关于创建的子进程,UNIX和windows

  1.一模一样的凡:进程创造后,父进程和子进程产生独家不同的地址空间(差不多鸣技术要求物理层面实现进程中内存的断),任何一个过程的于那地址空间受到之改动都无见面影响及另外一个历程。

  2.例外之是:在UNIX中,子进程的初步地址空间是父亲进程的一个副本,提示:子进程和父进程是可以来特读的共享内存区之。但是于windows系统来说,从同开始大进程与子进程的地方空间就不同之。

  抛开玩游戏成瘾,熬夜即几只话题,我想表达的是,我在没深刻玩这游乐之景况下说了同一句“不纵打打杀杀的为,有啊好玩的”,当自身真的去玩的上,

六 进程的停(了解)

  1.
正常化退出(自愿,如用户点击交互式页面的叉号,或程序执行完毕调用发起系统调用正常退出,在linux中用exit,在windows中用ExitProcess)

  2. 错退出(自愿,python a.py中a.py不在)

  3.
严重错误(非自愿,执行非法命令,如援不在的内存,1/0顶,可以捕捉异常,try…except…)

  4. 于另外进程杀死(非自愿,如kill -9)

 

本人之传统就成为了“就是打打杀杀的才好游戏,越杀得热烈越好玩”。

七 进程的层次结构

  无论UNIX还是windows,进程就来一个父进程,不同之是:

  1.
当UNIX中所有的长河,都是因init进程也清,组成树形结构。父子进程同组成一个进程组,这样,当从键盘发出一个信号时,该信号为送给当前跟键盘相关的历程组中的所有成员。

  2.
以windows中,没有经过层次之概念,所有的进程都是位平等之,唯一类似于经过层次之授意,是当创立过程时,父进程取得一个特别的令牌(名叫句柄),该词柄可以据此来控制子进程,但是父进程有权将该词柄传被其他子进程,这样就从未有过层次了。

 

八 进程的状态

  tail -f access.log |grep ‘404’

  执行程序tail,开启一个子过程,执行顺序grep,开启另外一个子进程,两独过程中因管道’|’通讯,将tail的结果作为grep的输入。

  进程grep在伺机输入(即I/O)时之状态叫做阻塞,此时grep命令还没法儿运转

  其实在片种植状态下会促成一个过程在逻辑上不能够运行,

  1.
过程挂于是本身原因,遇到I/O阻塞,便使为生CPU让别进程去执行,这样保证CPU一直当干活

  2.
及经过无关,是操作系统层面,可能会见盖一个过程占用时间了多,或者优先级等由,而调用其他的长河去下CPU。

  因而一个经过由三种状态

王者荣耀 2

  任何工作也是这么,当你还没开的时节,最好不要超前下定论。

九 进程并发的兑现(了解)

  进程并发的实现在,硬件中断一个正值运行的经过,把这儿经过运行的装有状态保存下去,为这,操作系统维护一摆放表,即进程表(process
table),每个过程占用一个进程表项(这些表项也称为进程控制块)

王者荣耀 3

  该表存放了经过状态的关键消息:程序计数器、堆栈指针、内存分配状况、所有打开文件的状态、帐号与调度信息,以及任何以经过由运行态转为就绪态或堵塞态时,必须保留的音信,从而确保该过程在还启航时,就像无为中断了同样。

 

亚、代码知识有

  

一 multiprocessing模块介绍:

python中的多线程无法运用基本上对优势,如果想要充分地动多核CPU的资源(os.cpu_count()查看),在python中大部分景用用多进程。Python提供了multiprocessing。
   
multiprocessing模块用来开启子进程,并在子进程被实践我们定制的职责(比如函数),该模块和大多线程模块threading的编程接口类似。

  multiprocessing模块的功力多多:支持子进程、通信和共享数据、执行不一样式之共同,提供了Process、Queue、Pipe、Lock等零件。

   
需要再行强调的一些凡:与线程不同,进程没有其它共享状态,进程修改的数额,改动仅限于该进程内。

 

其次 Process类的介绍

 创过程的好像

Process([group [, target [, name [, args [, kwargs]]]]]),由该类实例化得到的对象,表示一个子进程中的任务(尚未启动)

强调:
1. 需要使用关键字的方式来指定参数
2. args指定的为传给target函数的位置参数,是一个元组形式,必须有逗号

  参数介绍:

group参数不下,值始终为None

target表示调用对象,即子进程使履行的职责

args表示调用对象的职务参数元组,args=(1,2,’egon’,)

kwargs表示调用对象的字典,kwargs={‘name’:’egon’,’age’:18}

name为子进程的名号

 

 办法介绍:

p.start():启动进程,并调用该子进程面临的p.run()
p.run():进程启动时运行的不二法门,正是她失去调用target指定的函数,我们于定义类的类似中肯定要兑现该办法

p.terminate():强制停止进程p,不会见进行任何清理操作,如果p创建了子进程,该子进程就变成了僵尸进程,使用该法要专门小心这种景象。如果p还保存了一个沿那么也用非会见让放,进而导致死锁
p.is_alive():如果p仍然运行,返回True

p.join([timeout]):主线程等待p终止(强调:是主线程处于等的状态,而p是处于运行的状态)。timeout是可选的超时时间,需要强调的凡,p.join只能join住start开启之过程,而非能够join住run开启的经过

属性介绍:

1 p.daemon:默认值为False,如果设为True,代表p为后台运行的守护进程,当p的父进程终止时,p也随之终止,并且设定为True后,p不能创建自己的新进程,必须在p.start()之前设置
2 
3 p.name:进程的名称
4 
5 p.pid:进程的pid
6 
7 p.exitcode:进程在运行时为None、如果为–N,表示被信号N结束(了解即可)
8 
9 p.authkey:进程的身份验证键,默认是由os.urandom()随机生成的32字符的字符串。这个键的用途是为涉及网络连接的底层进程间通信提供安全性,这类连接只有在具有相同的身份验证键时才能成功(了解即可)

老三 Process类的用

专注:在windows中Process()必须坐# if __name__ ==
‘__main__’:下

Since Windows has no fork, the multiprocessing module starts a new Python process and imports the calling module. 
If Process() gets called upon import, then this sets off an infinite succession of new processes (or until your machine runs out of resources). 
This is the reason for hiding calls to Process() inside

if __name__ == "__main__"
since statements inside this if-statement will not get called upon import.
由于Windows没有fork,多处理模块启动一个新的Python进程并导入调用模块。 
如果在导入时调用Process(),那么这将启动无限继承的新进程(或直到机器耗尽资源)。 
这是隐藏对Process()内部调用的原,使用if __name__ == “__main __”,这个if语句中的语句将不会在导入时被调用。

创立并起启子进程的蝇头栽办法

#开进程的方法一:
import time
import random
from multiprocessing import Process
def piao(name):
    print('%s piaoing' %name)
    time.sleep(random.randrange(1,5))
    print('%s piao end' %name)



p1=Process(target=piao,args=('egon',)) #必须加,号
p2=Process(target=piao,args=('alex',))
p3=Process(target=piao,args=('wupeqi',))
p4=Process(target=piao,args=('yuanhao',))

p1.start()
p2.start()
p3.start()
p4.start()
print('主线程')

方法一

#开进程的方法二:
import time
import random
from multiprocessing import Process


class Piao(Process):
    def __init__(self,name):
        super().__init__()
        self.name=name
    def run(self):
        print('%s piaoing' %self.name)

        time.sleep(random.randrange(1,5))
        print('%s piao end' %self.name)

p1=Piao('egon')
p2=Piao('alex')
p3=Piao('wupeiqi')
p4=Piao('yuanhao')

p1.start() #start会自动调用run
p2.start()
p3.start()
p4.start()
print('主线程')

习1:把所法的socket通信变成并发的款型

from socket import *
from multiprocessing import Process

server=socket(AF_INET,SOCK_STREAM)
server.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
server.bind(('127.0.0.1',8080))
server.listen(5)

def talk(conn,client_addr):
    while True:
        try:
            msg=conn.recv(1024)
            if not msg:break
            conn.send(msg.upper())
        except Exception:
            break

if __name__ == '__main__': #windows下start进程一定要写到这下面
    while True:
        conn,client_addr=server.accept()
        p=Process(target=talk,args=(conn,client_addr))
        p.start()

server端

from socket import *

client=socket(AF_INET,SOCK_STREAM)
client.connect(('127.0.0.1',8080))


while True:
    msg=input('>>: ').strip()
    if not msg:continue

    client.send(msg.encode('utf-8'))
    msg=client.recv(1024)
    print(msg.decode('utf-8'))

多个client端

每来一个客户端,都在服务端开启一个进程,如果并发来一个万个客户端,要开启一万个进程吗,你自己尝试着在你自己的机器上开启一万个,10万个进程试一试。
解决方法:进程池

Process对象的join方法

join:主进程等,等待子进程结束

from multiprocessing import Process
import time
import random

class Piao(Process):
    def __init__(self,name):
        self.name=name
        super().__init__()
    def run(self):
        print('%s is piaoing' %self.name)
        time.sleep(random.randrange(1,3))
        print('%s is piao end' %self.name)


p=Piao('egon')
p.start()
p.join(0.0001) #等待p停止,等0.0001秒就不再等了
print('开始')

起了join,程序不就是串行了啊???

from multiprocessing import Process
import time
import random
def piao(name):
    print('%s is piaoing' %name)
    time.sleep(random.randint(1,3))
    print('%s is piao end' %name)

p1=Process(target=piao,args=('egon',))
p2=Process(target=piao,args=('alex',))
p3=Process(target=piao,args=('yuanhao',))
p4=Process(target=piao,args=('wupeiqi',))

p1.start()
p2.start()
p3.start()
p4.start()

#有的同学会有疑问:既然join是等待进程结束,那么我像下面这样写,进程不就又变成串行的了吗?
#当然不是了,必须明确:p.join()是让谁等?
#很明显p.join()是让主线程等待p的结束,卡住的是主线程而绝非进程p,

#详细解析如下:
#进程只要start就会在开始运行了,所以p1-p4.start()时,系统中已经有四个并发的进程了
#而我们p1.join()是在等p1结束,没错p1只要不结束主线程就会一直卡在原地,这也是问题的关键
#join是让主线程等,而p1-p4仍然是并发执行的,p1.join的时候,其余p2,p3,p4仍然在运行,等#p1.join结束,可能p2,p3,p4早已经结束了,这样p2.join,p3.join.p4.join直接通过检测,无需等待
# 所以4个join花费的总时间仍然是耗费时间最长的那个进程运行的时间
p1.join()
p2.join()
p3.join()
p4.join()

print('主线程')


#上述启动进程与join进程可以简写为
# p_l=[p1,p2,p3,p4]
# 
# for p in p_l:
#     p.start()
# 
# for p in p_l:
#     p.join()

 

Process对象的旁方法还是性能(了解)

terminate与is_alive

#进程对象的其他方法一:terminate,is_alive
from multiprocessing import Process
import time
import random

class Piao(Process):
    def __init__(self,name):
        self.name=name
        super().__init__()

    def run(self):
        print('%s is piaoing' %self.name)
        time.sleep(random.randrange(1,5))
        print('%s is piao end' %self.name)


p1=Piao('egon1')
p1.start()

p1.terminate()#关闭进程,不会立即关闭,所以is_alive立刻查看的结果可能还是存活
print(p1.is_alive()) #结果为True

print('开始')
print(p1.is_alive()) #结果为False

name与pid

from multiprocessing import Process
import time
import random
class Piao(Process):
    def __init__(self,name):
        # self.name=name
        # super().__init__() #Process的__init__方法会执行self.name=Piao-1,
        #                    #所以加到这里,会覆盖我们的self.name=name

        #为我们开启的进程设置名字的做法
        super().__init__()
        self.name=name

    def run(self):
        print('%s is piaoing' %self.name)
        time.sleep(random.randrange(1,3))
        print('%s is piao end' %self.name)

p=Piao('egon')
p.start()
print('开始')
print(p.pid) #查看pid

 

季 守护进程

主进程创造守护进程

  其一:守护进程会于主进程代码执行结束晚便止

  其二:守护进程内无法再次开始启子进程,否则抛出异常:AssertionError:
daemonic processes are not allowed to have children

顾:进程中是互相独立的,主进程代码运行结束,守护进程就终止

 

from multiprocessing import Process
import time
import random

class Piao(Process):
    def __init__(self,name):
        self.name=name
        super().__init__()
    def run(self):
        print('%s is piaoing' %self.name)
        time.sleep(random.randrange(1,3))
        print('%s is piao end' %self.name)


p=Piao('egon')
p.daemon=True #一定要在p.start()前设置,设置p为守护进程,禁止p创建子进程,并且父进程代码执行结束,p即终止运行
p.start()
print('主')

#主进程代码运行完毕,守护进程就会结束
from multiprocessing import Process
from threading import Thread
import time
def foo():
    print(123)
    time.sleep(1)
    print("end123")

def bar():
    print(456)
    time.sleep(3)
    print("end456")


p1=Process(target=foo)
p2=Process(target=bar)

p1.daemon=True
p1.start()
p2.start()
print("main-------") #打印该行则主进程代码结束,则守护进程p1应该被终止,可能会有p1任务执行的打印信息123,因为主进程打印main----时,p1也执行了,但是随即被终止

迷惑人的例子

五 进程同步(锁)

经过中数据未共享,但是共享同一仿文件系统,所以看与一个文本,或与一个打印终端,是从未有过问题之,

竞争带来的结果就是是乱,如何支配,就是加锁处理

part1:多个经过共享同一打印终端

并发运行,效率高,但竞争同一打印终端,带来了打印错乱

#并发运行,效率高,但竞争同一打印终端,带来了打印错乱
from multiprocessing import Process
import os,time
def work():
    print('%s is running' %os.getpid())
    time.sleep(2)
    print('%s is done' %os.getpid())

if __name__ == '__main__':
    for i in range(3):
        p=Process(target=work)
        p.start()

加锁:由并发变成了串行,牺牲了运行效率,但避免了竞争

#由并发变成了串行,牺牲了运行效率,但避免了竞争
from multiprocessing import Process,Lock
import os,time
def work(lock):
    lock.acquire()
    print('%s is running' %os.getpid())
    time.sleep(2)
    print('%s is done' %os.getpid())
    lock.release()
if __name__ == '__main__':
    lock=Lock()
    for i in range(3):
        p=Process(target=work,args=(lock,))
        p.start()

part2:多单过程共享同一文件

文本当数据库,模拟抢票

并发运行,效率高,但竞争写同一文件,数据写入错乱

#文件db的内容为:{"count":1}
#注意一定要用双引号,不然json无法识别
from multiprocessing import Process,Lock
import time,json,random
def search():
    dic=json.load(open('db.txt'))
    print('\033[43m剩余票数%s\033[0m' %dic['count'])

def get():
    dic=json.load(open('db.txt'))
    time.sleep(0.1) #模拟读数据的网络延迟
    if dic['count'] >0:
        dic['count']-=1
        time.sleep(0.2) #模拟写数据的网络延迟
        json.dump(dic,open('db.txt','w'))
        print('\033[43m购票成功\033[0m')

def task(lock):
    search()
    get()
if __name__ == '__main__':
    lock=Lock()
    for i in range(100): #模拟并发100个客户端抢票
        p=Process(target=task,args=(lock,))
        p.start()

加锁:购票行为由并发变成了串行,牺牲了运行效率,但保证了数据安全

#文件db的内容为:{"count":1}
#注意一定要用双引号,不然json无法识别
from multiprocessing import Process,Lock
import time,json,random
def search():
    dic=json.load(open('db.txt'))
    print('\033[43m剩余票数%s\033[0m' %dic['count'])

def get():
    dic=json.load(open('db.txt'))
    time.sleep(0.1) #模拟读数据的网络延迟
    if dic['count'] >0:
        dic['count']-=1
        time.sleep(0.2) #模拟写数据的网络延迟
        json.dump(dic,open('db.txt','w'))
        print('\033[43m购票成功\033[0m')

def task(lock):
    search()
    lock.acquire()
    get()
    lock.release()
if __name__ == '__main__':
    lock=Lock()
    for i in range(100): #模拟并发100个客户端抢票
        p=Process(target=task,args=(lock,))
        p.start()

总结:

加锁可以确保多单过程修改及同块数据时,同一时间只能发出一个职责可以展开改动,即串行的改动,没错,速度是缓了,但牺牲了快却保证了数额安全。
尽管如此足就此文件共享数据实现进程之中通信,但问题是:
1.效率低
2.需好加锁处理

 

啊是mutiprocessing模块为咱提供了基于消息的IPC通信机制:队列和管道。
1 队列和管道还是拿数据存放于外存中
2 队列又是因(管道+锁)实现的,可以叫咱们于犬牙交错的锁问题备受解脱出来,
咱俩该尽量避免使用共享数据,尽可能用消息传递和班,避免处理千头万绪的联手和钉问题,而且以经过数目增加时,往往可赢得更好的可获展性。

 

六 队列(推荐应用)

  
进程彼此之间互相隔离,要促成进程中通信(IPC),multiprocessing模块支持有限种植形式:队列和管道,这有限种方法还是利用消息传递的

 创建行的切近(底层就是因管道与锁定的措施实现)

1 Queue([maxsize]):创建共享的进程队列,Queue是多进程安全的队列,可以使用Queue实现多进程之间的数据传递。 

    参数介绍:

1 maxsize是队列中允许最大项数,省略则无大小限制。    

  方法介绍:

    主要措施:

q.put方法用以插入数据到队列中,put方法还有两个可选参数:blocked和timeout。如果blocked为True(默认值),并且timeout为正值,该方法会阻塞timeout指定的时间,直到该队列有剩余的空间。如果超时,会抛出Queue.Full异常。如果blocked为False,但该Queue已满,会立即抛出Queue.Full异常。
q.get方法可以从队列读取并且删除一个元素。同样,get方法有两个可选参数:blocked和timeout。如果blocked为True(默认值),并且timeout为正值,那么在等待时间内没有取到任何元素,会抛出Queue.Empty异常。如果blocked为False,有两种情况存在,如果Queue有一个值可用,则立即返回该值,否则,如果队列为空,则立即抛出Queue.Empty异常.

q.get_nowait():同q.get(False)
q.put_nowait():同q.put(False)

q.empty():调用此方法时q为空则返回True,该结果不可靠,比如在返回True的过程中,如果队列中又加入了项目。
q.full():调用此方法时q已满则返回True,该结果不可靠,比如在返回True的过程中,如果队列中的项目被取走。
q.qsize():返回队列中目前项目的正确数量,结果也不可靠,理由同q.empty()和q.full()一样

另外方式(了解):

1 q.cancel_join_thread():不会在进程退出时自动连接后台线程。可以防止join_thread()方法阻塞
2 q.close():关闭队列,防止队列中加入更多数据。调用此方法,后台线程将继续写入那些已经入队列但尚未写入的数据,但将在此方法完成时马上关闭。如果q被垃圾收集,将调用此方法。关闭队列不会在队列使用者中产生任何类型的数据结束信号或异常。例如,如果某个使用者正在被阻塞在get()操作上,关闭生产者中的队列不会导致get()方法返回错误。
3 q.join_thread():连接队列的后台线程。此方法用于在调用q.close()方法之后,等待所有队列项被消耗。默认情况下,此方法由不是q的原始创建者的所有进程调用。调用q.cancel_join_thread方法可以禁止这种行为

  应用:

'''
multiprocessing模块支持进程间通信的两种主要形式:管道和队列
都是基于消息传递实现的,但是队列接口
'''

from multiprocessing import Process,Queue
import time
q=Queue(3)


#put ,get ,put_nowait,get_nowait,full,empty
q.put(3)
q.put(3)
q.put(3)
print(q.full()) #满了

print(q.get())
print(q.get())
print(q.get())
print(q.empty()) #空了

生产者消费者模型

于产出编程中利用生产者与顾客模式会解决大部分起问题。该模式通过平衡生产线程和消费线程的干活力量来增强程序的整体处理数量的快。

    为什么要使生产者和消费者模式

当线程世界里,生产者就是产数量的线程,消费者即使是消费数据的线程。在差不多线程开发中,如果劳动者处理速度很快,而顾客处理速度很缓慢,那么生产者就得等顾客处理完,才能够持续生产数据。同样的理,如果消费者的拍卖能力超过生产者,那么消费者就亟须等生产者。为了化解之题目于是引入了劳动者与消费者模式。

    什么是劳动者消费者模式

劳动者消费者模式是由此一个容器来缓解劳动者和顾客的强耦合问题。生产者和买主彼此之间不直通讯,而经阻塞队列来展开报道,所以生产者生产完毕数据后不要等顾客处理,直接丢掉给卡住队列,消费者莫找生产者要多少,而是径直由绿灯队列里落,阻塞队列就相当给一个缓冲区,平衡了劳动者和消费者之处理能力。

冲队列实现生产者消费者模型

from multiprocessing import Process,Queue
import time,random,os
def consumer(q):
    while True:
        res=q.get()
        time.sleep(random.randint(1,3))
        print('\033[45m%s 吃 %s\033[0m' %(os.getpid(),res))

def producer(q):
    for i in range(10):
        time.sleep(random.randint(1,3))
        res='包子%s' %i
        q.put(res)
        print('\033[44m%s 生产了 %s\033[0m' %(os.getpid(),res))

if __name__ == '__main__':
    q=Queue()
    #生产者们:即厨师们
    p1=Process(target=producer,args=(q,))

    #消费者们:即吃货们
    c1=Process(target=consumer,args=(q,))

    #开始
    p1.start()
    c1.start()
    print('主')

这儿之题目是主进程永远不会见结束,原因是:生产者p在生了晚哪怕结了,但是顾客c在取空了q之后,则直处在死循环中都卡在q.get()这同步。

解决办法就是被劳动者在生了后,往队列中又作一个了事信号,这样消费者以接到到结束信号后即可以break出死循环

 

生产者在生产完毕后发送结束信号None

from multiprocessing import Process,Queue
import time,random,os
def consumer(q):
    while True:
        res=q.get()
        if res is None:break #收到结束信号则结束
        time.sleep(random.randint(1,3))
        print('\033[45m%s 吃 %s\033[0m' %(os.getpid(),res))

def producer(q):
    for i in range(10):
        time.sleep(random.randint(1,3))
        res='包子%s' %i
        q.put(res)
        print('\033[44m%s 生产了 %s\033[0m' %(os.getpid(),res))
    q.put(None) #发送结束信号
if __name__ == '__main__':
    q=Queue()
    #生产者们:即厨师们
    p1=Process(target=producer,args=(q,))

    #消费者们:即吃货们
    c1=Process(target=consumer,args=(q,))

    #开始
    p1.start()
    c1.start()
    print('主')

瞩目:结束信号None,不必然要出于生产者发,主进程里同好发,但主进程要等生产者结束晚才应该发送该信号

主进程在生产者生产完毕后发送结束信号None

from multiprocessing import Process,Queue
import time,random,os
def consumer(q):
    while True:
        res=q.get()
        if res is None:break #收到结束信号则结束
        time.sleep(random.randint(1,3))
        print('\033[45m%s 吃 %s\033[0m' %(os.getpid(),res))

def producer(q):
    for i in range(2):
        time.sleep(random.randint(1,3))
        res='包子%s' %i
        q.put(res)
        print('\033[44m%s 生产了 %s\033[0m' %(os.getpid(),res))

if __name__ == '__main__':
    q=Queue()
    #生产者们:即厨师们
    p1=Process(target=producer,args=(q,))

    #消费者们:即吃货们
    c1=Process(target=consumer,args=(q,))

    #开始
    p1.start()
    c1.start()

    p1.join()
    q.put(None) #发送结束信号
    print('主')

可上述解决措施,在出差不多只生产者与多只顾客常常,我们则要用一个要命low的措施去解决

有几个生产者就需要发送几次结束信号:相当low

from multiprocessing import Process,Queue
import time,random,os
def consumer(q):
    while True:
        res=q.get()
        if res is None:break #收到结束信号则结束
        time.sleep(random.randint(1,3))
        print('\033[45m%s 吃 %s\033[0m' %(os.getpid(),res))

def producer(name,q):
    for i in range(2):
        time.sleep(random.randint(1,3))
        res='%s%s' %(name,i)
        q.put(res)
        print('\033[44m%s 生产了 %s\033[0m' %(os.getpid(),res))



if __name__ == '__main__':
    q=Queue()
    #生产者们:即厨师们
    p1=Process(target=producer,args=('包子',q))
    p2=Process(target=producer,args=('骨头',q))
    p3=Process(target=producer,args=('泔水',q))

    #消费者们:即吃货们
    c1=Process(target=consumer,args=(q,))
    c2=Process(target=consumer,args=(q,))

    #开始
    p1.start()
    p2.start()
    p3.start()
    c1.start()

    p1.join() #必须保证生产者全部生产完毕,才应该发送结束信号
    p2.join()
    p3.join()
    q.put(None) #有几个生产者就应该发送几次结束信号None
    q.put(None) #发送结束信号
    q.put(None) #发送结束信号
    print('主')

 

实则我们的思绪才是殡葬完信号而已,有另外一种植队列提供了这种体制

#JoinableQueue([maxsize]):这就像是一个Queue对象,但队列允许项目的使用者通知生成者项目已经被成功处理。通知进程是使用共享的信号和条件变量来实现的。

   #参数介绍:
    maxsize是队列中允许最大项数,省略则无大小限制。    
  #方法介绍:
    JoinableQueue的实例p除了与Queue对象相同的方法之外还具有:
    q.task_done():使用者使用此方法发出信号,表示q.get()的返回项目已经被处理。如果调用此方法的次数大于从队列中删除项目的数量,将引发ValueError异常
    q.join():生产者调用此方法进行阻塞,直到队列中所有的项目均被处理。阻塞将持续到队列中的每个项目均调用q.task_done()方法为止

from multiprocessing import Process,JoinableQueue
import time,random,os
def consumer(q):
    while True:
        res=q.get()
        time.sleep(random.randint(1,3))
        print('\033[45m%s 吃 %s\033[0m' %(os.getpid(),res))

        q.task_done() #向q.join()发送一次信号,证明一个数据已经被取走了

def producer(name,q):
    for i in range(10):
        time.sleep(random.randint(1,3))
        res='%s%s' %(name,i)
        q.put(res)
        print('\033[44m%s 生产了 %s\033[0m' %(os.getpid(),res))
    q.join()


if __name__ == '__main__':
    q=JoinableQueue()
    #生产者们:即厨师们
    p1=Process(target=producer,args=('包子',q))
    p2=Process(target=producer,args=('骨头',q))
    p3=Process(target=producer,args=('泔水',q))

    #消费者们:即吃货们
    c1=Process(target=consumer,args=(q,))
    c2=Process(target=consumer,args=(q,))
    c1.daemon=True
    c2.daemon=True

    #开始
    p_l=[p1,p2,p3,c1,c2]
    for p in p_l:
        p.start()

    p1.join()
    p2.join()
    p3.join()
    print('主') 

    #主进程等--->p1,p2,p3等---->c1,c2
    #p1,p2,p3结束了,证明c1,c2肯定全都收完了p1,p2,p3发到队列的数据
    #因而c1,c2也没有存在的价值了,应该随着主进程的结束而结束,所以设置成守护进程

七 管道

经过王者荣耀之中通信(IPC)方式二:管道(不推荐以,了解即可)

 

#创建管道的类:
Pipe([duplex]):在进程之间创建一条管道,并返回元组(conn1,conn2),其中conn1,conn2表示管道两端的连接对象,强调一点:必须在产生Process对象之前产生管道
#参数介绍:
dumplex:默认管道是全双工的,如果将duplex射成False,conn1只能用于接收,conn2只能用于发送。
#主要方法:
    conn1.recv():接收conn2.send(obj)发送的对象。如果没有消息可接收,recv方法会一直阻塞。如果连接的另外一端已经关闭,那么recv方法会抛出EOFError。
    conn1.send(obj):通过连接发送对象。obj是与序列化兼容的任意对象
 #其他方法:
conn1.close():关闭连接。如果conn1被垃圾回收,将自动调用此方法
conn1.fileno():返回连接使用的整数文件描述符
conn1.poll([timeout]):如果连接上的数据可用,返回True。timeout指定等待的最长时限。如果省略此参数,方法将立即返回结果。如果将timeout射成None,操作将无限期地等待数据到达。

conn1.recv_bytes([maxlength]):接收c.send_bytes()方法发送的一条完整的字节消息。maxlength指定要接收的最大字节数。如果进入的消息,超过了这个最大值,将引发IOError异常,并且在连接上无法进行进一步读取。如果连接的另外一端已经关闭,再也不存在任何数据,将引发EOFError异常。
conn.send_bytes(buffer [, offset [, size]]):通过连接发送字节数据缓冲区,buffer是支持缓冲区接口的任意对象,offset是缓冲区中的字节偏移量,而size是要发送字节数。结果数据以单条消息的形式发出,然后调用c.recv_bytes()函数进行接收    

conn1.recv_bytes_into(buffer [, offset]):接收一条完整的字节消息,并把它保存在buffer对象中,该对象支持可写入的缓冲区接口(即bytearray对象或类似的对象)。offset指定缓冲区中放置消息处的字节位移。返回值是收到的字节数。如果消息长度大于可用的缓冲区空间,将引发BufferTooShort异常。

介绍

 

基于管道实现进程间通信(与队列的方式是类似的,队列就是管道加锁实现的)

from multiprocessing import Process,Pipe

import time,os
def consumer(p,name):
    left,right=p
    left.close()
    while True:
        try:
            baozi=right.recv()
            print('%s 收到包子:%s' %(name,baozi))
        except EOFError:
            right.close()
            break
def producer(seq,p):
    left,right=p
    right.close()
    for i in seq:
        left.send(i)
        # time.sleep(1)
    else:
        left.close()
if __name__ == '__main__':
    left,right=Pipe()

    c1=Process(target=consumer,args=((left,right),'c1'))
    c1.start()


    seq=(i for i in range(10))
    producer(seq,(left,right))

    right.close()
    left.close()

    c1.join()
    print('主进程')

只顾:生产者与消费者都尚未采用管道的某个端点,就当用那关闭,如在劳动者中关闭管道的右端,在消费者遭遇关闭管道的左端。如果忘记执行这些手续,程序可能再度消费者被的recv()操作上悬挂于。管道是由操作系统进行引用计数的,必须以备进程被关闭管道后才会生产EOFError异常。因此于劳动者中关闭管道不会见时有发生任何力量,付费消费者受到吗关闭了相同之管道端点。

 

管道可以用于双向通信,利用通常在客户端/服务器中使用的请求/响应模型或远程过程调用,就可以使用管道编写与进程交互的程序

from multiprocessing import Process,Pipe

import time,os
def adder(p,name):
    server,client=p
    client.close()
    while True:
        try:
            x,y=server.recv()
        except EOFError:
            server.close()
            break
        res=x+y
        server.send(res)
    print('server done')
if __name__ == '__main__':
    server,client=Pipe()

    c1=Process(target=adder,args=((server,client),'c1'))
    c1.start()

    server.close()

    client.send((10,20))
    print(client.recv())
    client.close()

    c1.join()
    print('主进程')
#注意:send()和recv()方法使用pickle模块对对象进行序列化。

 

八 共享数据

展望未来,基于消息传递的面世编程是肯定

纵然是用线程,推荐做法吧是用次第设计呢大气独自的线程集合

由此信息队列交换数据。这样大地抽了针对运锁定和其他一起手段的需,

尚得扩大至分布式系统中

进程之中通信应该尽量避免使用本节所摆的共享数据的章程

进程间数据是独立的,可以借助于队列或管道实现通信,二者都是基于消息传递的

虽然进程间数据独立,但可以通过Manager实现数据共享,事实上Manager的功能远不止于此

A manager object returned by Manager() controls a server process which holds Python objects and allows other processes to manipulate them using proxies.

A manager returned by Manager() will support types list, dict, Namespace, Lock, RLock, Semaphore, BoundedSemaphore, Condition, Event, Barrier, Queue, Value and Array. For example,

#进程之间操作共享的数据

from multiprocessing import Manager,Process,Lock
import os
def work(d,lock):
    # with lock: #不加锁而操作共享的数据,肯定会出现数据错乱
        d['count']-=1

if __name__ == '__main__':
    lock=Lock()
    with Manager() as m:
        dic=m.dict({'count':100})
        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)
        #{'count': 94}

 

九 信号量(了解)

互斥锁 同时只允许一个线程更改数据,而Semaphore是同时允许一定数量的线程更改数据 ,比如厕所有3个坑,那最多只允许3个人上厕所,后面的人只能等里面有人出来了才能再进去,如果指定信号量为3,那么来一个人获得一把锁,计数加1,当计数等于3时,后面的人均需要等待。一旦释放,就有人可以获得一把锁

    信号量与进程池的概念很像,但是要区分开,信号量涉及到加锁的概念

from multiprocessing import Process,Semaphore
import time,random

def go_wc(sem,user):
    sem.acquire()
    print('%s 占到一个茅坑' %user)
    time.sleep(random.randint(0,3)) #模拟每个人拉屎速度不一样,0代表有的人蹲下就起来了
    sem.release()

if __name__ == '__main__':
    sem=Semaphore(5)
    p_l=[]
    for i in range(13):
        p=Process(target=go_wc,args=(sem,'user%s' %i,))
        p.start()
        p_l.append(p)

    for i in p_l:
        i.join()
    print('============》')

信号量Semahpore(同线程一样)

十 事件(了解)

python线程的事件用于主线程控制其他线程的执行,事件主要提供了三个方法 set、wait、clear。

    事件处理的机制:全局定义了一个“Flag”,如果“Flag”值为 False,那么当程序执行 event.wait 方法时就会阻塞,如果“Flag”值为True,那么event.wait 方法时便不再阻塞。

clear:将“Flag”设置为False
set:将“Flag”设置为True


#_*_coding:utf-8_*_
#!/usr/bin/env python

from multiprocessing import Process,Event
import time,random

def car(e,n):
    while True:
        if not e.is_set(): #Flase
            print('\033[31m红灯亮\033[0m,car%s等着' %n)
            e.wait()
            print('\033[32m车%s 看见绿灯亮了\033[0m' %n)
            time.sleep(random.randint(3,6))
            if not e.is_set():
                continue
            print('走你,car', n)
            break

def police_car(e,n):
    while True:
        if not e.is_set():
            print('\033[31m红灯亮\033[0m,car%s等着' % n)
            e.wait(1)
            print('灯的是%s,警车走了,car %s' %(e.is_set(),n))
            break

def traffic_lights(e,inverval):
    while True:
        time.sleep(inverval)
        if e.is_set():
            e.clear() #e.is_set() ---->False
        else:
            e.set()

if __name__ == '__main__':
    e=Event()
    # for i in range(10):
    #     p=Process(target=car,args=(e,i,))
    #     p.start()

    for i in range(5):
        p = Process(target=police_car, args=(e, i,))
        p.start()
    t=Process(target=traffic_lights,args=(e,10))
    t.start()

    print('============》')

Event(同线程一样)

 

十一 进程池

以动用Python进行系统管理的当儿,特别是以操作多个文件目录,或者远程控制多宝主机,并行操作可以节省大量底时。多进程是贯彻产出的一手有,需要小心的问题是:

  1. 很明显要出现执行的天职便如多甚叫核数
  2. 一个操作系统不可能不过开启进程,通常有几乎单按就从头几单经过
  3. 过程被了多,效率反而会骤降(开启进程是内需占用系统资源的,而且打开多余核数目的长河也无力回天形成互相)

如当于操作对象数目不大时,可以直接动用multiprocessing中的Process动态成生多个经过,十几个还吓,但要是是多只,上千只。。。手动的夺限制进程数量却以最为过繁琐,此时得以发表上程池的功效。

咱便得经保护一个经过池来控制过程数目,比如httpd的长河模式,规定极小进程数与极老进程数… 
ps:对于远程过程调用的高等应用程序而言,应该采取进程池,Pool可以供指定数量的经过,供用户调用,当起新的乞求提交至pool中常,如果池还不曾满,那么就会创一个初的进程之所以来推行该要;但如若池中之经过数都达规定极可怜价值,那么该要虽见面等,直到池中生过程结束,就收录进程池中的经过。

 
  创建进程池的类似:如果指定numprocess为3,则经过池会从无至有创造三个经过,然后自始至终使用即时三单过程去履行有任务,不会见打开其他进程

1 Pool([numprocess  [,initializer [, initargs]]]):创建进程池 

    参数介绍:

1 numprocess:要创建的进程数,如果省略,将默认使用cpu_count()的值
2 initializer:是每个工作进程启动时要执行的可调用对象,默认为None
3 initargs:是要传给initializer的参数组

  方法介绍:

    主要措施:

p.apply(func [, args [, kwargs]]):在一个池工作进程中执行func(*args,**kwargs),然后返回结果。需要强调的是:此操作并不会在所有池工作进程中并执行func函数。如果要通过不同参数并发地执行func函数,必须从不同线程调用p.apply()函数或者使用p.apply_async()
p.apply_async(func [, args [, kwargs]]):在一个池工作进程中执行func(*args,**kwargs),然后返回结果。此方法的结果是AsyncResult类的实例,callback是可调用对象,接收输入参数。当func的结果变为可用时,将理解传递给callback。callback禁止执行任何阻塞操作,否则将接收其他异步操作中的结果。

p.close():关闭进程池,防止进一步操作。如果所有操作持续挂起,它们将在工作进程终止前完成
P.jion():等待所有工作进程退出。此方法只能在close()或teminate()之后调用

 其他艺术(了解一些)

方法apply_async()和map_async()的返回值是AsyncResul的实例obj。实例具有以下方法
obj.get():返回结果,如果有必要则等待结果到达。timeout是可选的。如果在指定时间内还没有到达,将引发一场。如果远程操作中引发了异常,它将在调用此方法时再次被引发。
obj.ready():如果调用完成,返回True
obj.successful():如果调用完成且没有引发异常,返回True,如果在结果就绪之前调用此方法,引发异常
obj.wait([timeout]):等待结果变为可用。
obj.terminate():立即终止所有工作进程,同时不执行任何清理或结束任何挂起工作。如果p被垃圾回收,将自动调用此函数

  应用:

apply同步执行:阻塞式

from multiprocessing import Pool
import os,time
def work(n):
    print('%s run' %os.getpid())
    time.sleep(3)
    return n**2

if __name__ == '__main__':
    p=Pool(3) #进程池中从无到有创建三个进程,以后一直是这三个进程在执行任务
    res_l=[]
    for i in range(10):
        res=p.apply(work,args=(i,)) #同步运行,阻塞、直到本次任务执行完毕拿到res
        res_l.append(res)
    print(res_l)

apply_async异步执行:非阻塞

from multiprocessing import Pool
import os,time
def work(n):
    print('%s run' %os.getpid())
    time.sleep(3)
    return n**2

if __name__ == '__main__':
    p=Pool(3) #进程池中从无到有创建三个进程,以后一直是这三个进程在执行任务
    res_l=[]
    for i in range(10):
        res=p.apply_async(work,args=(i,)) #同步运行,阻塞、直到本次任务执行完毕拿到res
        res_l.append(res)

    #异步apply_async用法:如果使用异步提交的任务,主进程需要使用jion,等待进程池内任务都处理完,然后可以用get收集结果,否则,主进程结束,进程池可能还没来得及执行,也就跟着一起结束了
    p.close()
    p.join()
    for res in res_l:
        print(res.get()) #使用get来获取apply_aync的结果,如果是apply,则没有get方法,因为apply是同步执行,立刻获取结果,也根本无需get

 

详解:apply_async与apply

#一:使用进程池(非阻塞,apply_async)
#coding: utf-8
from multiprocessing import Process,Pool
import time

def func(msg):
    print( "msg:", msg)
    time.sleep(1)
    return msg

if __name__ == "__main__":
    pool = Pool(processes = 3)
    res_l=[]
    for i in range(10):
        msg = "hello %d" %(i)
        res=pool.apply_async(func, (msg, ))   #维持执行的进程总数为processes,当一个进程执行完毕后会添加新的进程进去
        res_l.append(res)
    print("==============================>") #没有后面的join,或get,则程序整体结束,进程池中的任务还没来得及全部执行完也都跟着主进程一起结束了

    pool.close() #关闭进程池,防止进一步操作。如果所有操作持续挂起,它们将在工作进程终止前完成
    pool.join()   #调用join之前,先调用close函数,否则会出错。执行完close后不会有新的进程加入到pool,join函数等待所有子进程结束

    print(res_l) #看到的是<multiprocessing.pool.ApplyResult object at 0x10357c4e0>对象组成的列表,而非最终的结果,但这一步是在join后执行的,证明结果已经计算完毕,剩下的事情就是调用每个对象下的get方法去获取结果
    for i in res_l:
        print(i.get()) #使用get来获取apply_aync的结果,如果是apply,则没有get方法,因为apply是同步执行,立刻获取结果,也根本无需get

#二:使用进程池(阻塞,apply)
#coding: utf-8
from multiprocessing import Process,Pool
import time

def func(msg):
    print( "msg:", msg)
    time.sleep(0.1)
    return msg

if __name__ == "__main__":
    pool = Pool(processes = 3)
    res_l=[]
    for i in range(10):
        msg = "hello %d" %(i)
        res=pool.apply(func, (msg, ))   #维持执行的进程总数为processes,当一个进程执行完毕后会添加新的进程进去
        res_l.append(res) #同步执行,即执行完一个拿到结果,再去执行另外一个
    print("==============================>")
    pool.close()
    pool.join()   #调用join之前,先调用close函数,否则会出错。执行完close后不会有新的进程加入到pool,join函数等待所有子进程结束

    print(res_l) #看到的就是最终的结果组成的列表
    for i in res_l: #apply是同步的,所以直接得到结果,没有get()方法
        print(i)

勤学苦练2:使用过程池维护稳定数目的过程(重写练习1)

 server端

#Pool内的进程数默认是cpu核数,假设为4(查看方法os.cpu_count())
#开启6个客户端,会发现2个客户端处于等待状态
#在每个进程内查看pid,会发现pid使用为4个,即多个客户端公用4个进程
from socket import *
from multiprocessing import Pool
import os

server=socket(AF_INET,SOCK_STREAM)
server.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
server.bind(('127.0.0.1',8080))
server.listen(5)

def talk(conn,client_addr):
    print('进程pid: %s' %os.getpid())
    while True:
        try:
            msg=conn.recv(1024)
            if not msg:break
            conn.send(msg.upper())
        except Exception:
            break

if __name__ == '__main__':
    p=Pool()
    while True:
        conn,client_addr=server.accept()
        p.apply_async(talk,args=(conn,client_addr))
        # p.apply(talk,args=(conn,client_addr)) #同步的话,则同一时间只有一个客户端能访问

 

客户端

from socket import *

client=socket(AF_INET,SOCK_STREAM)
client.connect(('127.0.0.1',8080))


while True:
    msg=input('>>: ').strip()
    if not msg:continue

    client.send(msg.encode('utf-8'))
    msg=client.recv(1024)
    print(msg.decode('utf-8'))

 

发觉:并发开启多独客户端,服务端同一时间只出3个例外之pid,干少一个客户端,另外一个客户端才见面进入,被3只过程有处理

 

  扭转少函数:

得回调函数的气象:进程池中另外一个任务要处理完毕了,就立即告诉主进程:我好了额,你得拍卖自己的结果了。主进程则调用一个函数去处理该结果,该函数即回调函数

我们可以拿吃时间(阻塞)的天职放到进程池中,然后指定回调函数(主进程负责履行),这样主进程在尽回调函数时就是省去了I/O的过程,直接以到之是职责之结果。

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 pasrse_page(res):
    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)
    res_l=[]
    for url in urls:
        res=p.apply_async(get_page,args=(url,),callback=pasrse_page)
        res_l.append(res)

    p.close()
    p.join()
    print([res.get() for res in res_l]) #拿到的是get_page的结果,其实完全没必要拿该结果,该结果已经传给回调函数处理了

'''
打印结果:
<进程3388> get https://www.baidu.com
<进程3389> get https://www.python.org
<进程3390> get https://www.openstack.org
<进程3388> get https://help.github.com/
<进程3387> parse https://www.baidu.com
<进程3389> get http://www.sina.com.cn/
<进程3387> parse https://www.python.org
<进程3387> parse https://help.github.com/
<进程3387> parse http://www.sina.com.cn/
<进程3387> parse https://www.openstack.org
[{'url': 'https://www.baidu.com', 'text': '<!DOCTYPE html>\r\n...',...}]
'''

爬虫案例

from multiprocessing import Pool
import time,random
import requests
import re

def get_page(url,pattern):
    response=requests.get(url)
    if response.status_code == 200:
        return (response.text,pattern)

def parse_page(info):
    page_content,pattern=info
    res=re.findall(pattern,page_content)
    for item in res:
        dic={
            'index':item[0],
            'title':item[1],
            'actor':item[2].strip()[3:],
            'time':item[3][5:],
            'score':item[4]+item[5]

        }
        print(dic)
if __name__ == '__main__':
    pattern1=re.compile(r'<dd>.*?board-index.*?>(\d+)<.*?title="(.*?)".*?star.*?>(.*?)<.*?releasetime.*?>(.*?)<.*?integer.*?>(.*?)<.*?fraction.*?>(.*?)<',re.S)

    url_dic={
        'http://maoyan.com/board/7':pattern1,
    }

    p=Pool()
    res_l=[]
    for url,pattern in url_dic.items():
        res=p.apply_async(get_page,args=(url,pattern),callback=parse_page)
        res_l.append(res)

    for i in res_l:
        i.get()

    # res=requests.get('http://maoyan.com/board/7')
    # print(re.findall(pattern,res.text))

假如以主进程中等过程池中有所任务还推行了后,再统一处理结果,则不管需回调函数

from multiprocessing import Pool
import time,random,os

def work(n):
    time.sleep(1)
    return n**2
if __name__ == '__main__':
    p=Pool()

    res_l=[]
    for i in range(10):
        res=p.apply_async(work,args=(i,))
        res_l.append(res)

    p.close()
    p.join() #等待进程池中所有进程执行完毕

    nums=[]
    for res in res_l:
        nums.append(res.get()) #拿到所有结果
    print(nums) #主进程拿到所有的处理结果,可以在主进程中进行统一进行处理

进程池的另实现方式:https://docs.python.org/dev/library/concurrent.futures.html

 

相关文章

发表评论

电子邮件地址不会被公开。 必填项已用*标注

*
*
Website