python 进程线程 day05

2018.8.10

目录

孤儿、僵尸进程

写一个聊天室

multiprocessing 模块创建进程


day05

孤儿、僵尸进程

  • 孤儿进程:当父进程先于子进程退出,此时子进程就会成为孤儿进程。
    孤儿进程会被系统指定进程收养,即系统进程会成为孤儿进程新的父进程,系统进程会自动处理孤儿进程退出状态
    import os 
    from time import sleep 
    
    pid = os.fork()
    
    if pid == 0:
        print("child PID:",os.getppid())
        sleep(2)
        print("parent PID again:",os.getppid())
    elif pid > 0:
        sleep(1)
        print("parent process:",os.getpid())
  • 僵尸进程:子进程先于父进程退出,父进程没有处理子进程的退出状态,此时子进程就会成为僵尸进程。
    僵尸进程会滞留部分PCB信息在内存中,大量的僵尸进程会消耗系统的内存资源,所以要尽量避免僵尸进程产生
    import os 
    from time import sleep 
    
    pid = os.fork()
    
    if pid == 0:
        print("child PID:",os.getpid())
        print("parent PID again:",os.getppid())
    elif pid > 0:
        print("parent process:",os.getpid())
        while True:
            pass 
    
    #ps -aux,查看子进程pid是否是z,(z代表子进程)

如何避免僵尸进程产生

  • 父进程先退出
  • 父进程处理子进程退出状态

     
  • pid,status = os.wait()
  • 功能:在父进程中阻塞等待处理子进程的退出
  • 返回值:
    pid 退出的那个子进程的PID号
    status 子进程的退出状态 
  • 获取原来退出状态
    os.WEXITSTATUS(status)
    import os 
    from time import sleep 
    
    pid = os.fork()
    
    if pid < 0:
        print("create process faild")
    elif pid == 0:
        print("Child process running")
        sleep(3)
        print("Child process over")
        os._exit(3)
    else:
        #等子进程执行完毕进行回收
        pid,status = os.wait()
        print(pid,status)
        print(os.WEXITSTATUS(status)) #原来退出状态
        while True:
            pass
    
    #Child process running
    #Child process over
    #3340 768
    #3
    

     
  • pid,status = os.waitpid(pid,option)
  • 功能:在父进程中阻塞等待处理子进程的退出
  • 参数:
    pid -1 表示等待任意子进程退出
         >0 表示等待对应PID号的子进程退出
        option  0 表示阻塞等待
                 WNOHANG 表示非阻塞
  • 返回值:
    pid 退出那个子进程的PID号
    status 子进程的退出状态
  • waitpid(-1,0) ===>wait()
    import os 
    from time import sleep 
    
    pid = os.fork()
    
    if pid < 0:
        print("create process faild")
    elif pid == 0:
        print("Child process running")
        sleep(3)
        print("Child process over")
        os._exit(3)
    else:
        while True:
            sleep(1)
            pid1,status = os.waitpid(-1,os.WNOHANG)
            print(pid1,status)
            if pid1 > 0:
                break 
    
        while True:
            pass
    
    


     
  • 创建二级子进程
  • 父进程创建子进程等待子进程退出
  • 子进程创建下一级子进程,然后立即退出
  • 二级子进程成为孤儿,处理具体工作
    #二级子进程处理僵尸问题
    
    import os 
    from time import sleep
    
    def fun1():
        sleep(3)
        print("第一件事情")
    
    def fun2():
        sleep(4)
        print("第二件事情")
    
    pid = os.fork()
    
    if pid < 0:
        print("create process failed")
    elif pid == 0:
        #创建二级子进程
        pid1 = os.fork()
        if pid1 == 0:
            fun2() #执行fun2
        elif pid1 > 0:
            os._exit(0) #子进程退出
    else:
        os.wait()
        fun1()

写一个聊天室

  功能:类似于qq群聊

  1. 进入聊天室需要输入姓名,姓名不能重复
  2. 有人进入聊天室此时会向其他人发起通知
    xxx进入了聊天室
  3. 如果一个人发消息,其它人都可以收到
    xxx说:xxxx
  4. 如果某个人退出聊天室其它人也会收到通知
    xxx 退出了聊天室
  5. 服务端可以喊话:此时群里所有人都能收到服务端消息
    管理员 说:xxx
  • 整体结构:分为几部分,如何封装,使用什么样的技术手段

     
  • 服务端
  • 客户端
  • 在客户端和服务端每个功能封装为一个函数
  • 技术方案:
    转发:一个客户发送给服务器,服务器发送给其他人
  • 套接字使用:udp完成操作
  • 用户存储:字典 或者 列表(可变类型,能够遍历提取)
                    地址  用户名
  • 发送和接收消息的控制:发送和接收使用多进程分离互不影响

     

  注意事项

  1. 注重封装
  2. 分段测试

  代码编写流程

  • 搭建通信--》创建多进程--》每个进程功能确定--》实现每一个功能模块

  功能细节梳理

  1. 进入聊天室
    1. 客户端:输入姓名
                    将信息发给我服务器  L name
    2. 服务端:接受消息
                    判断请求类型
                    判断是否可以登录(姓名是否已经存在)
                    返回给客户端是否登录(如果可以服务端会将姓名插入到存储结构)
                    给所有人发送消息
  2.   聊天
    1. 客户端:发起聊天 c msg
                    接收服务器回复
    2. 服务端:接受消息
                    判断消息类型
                    组织消息结构转发给其他客户端
  3.   退出聊天室
    1. 客户端:发送消息退出 Q name
                    接受服务
                    退出程序
    2. 服务器:接收消息
                    判断请求类型
                    从用户结构删除对应用户
                    告知所有人 xxx退出

服务器:

from socket import * 
import os,sys

#发送管理员消息
def do_child(s,addr):
    while True:
        msg = input("管理员消息:")
        msg = "C 管理员 " + msg 
        s.sendto(msg.encode(),addr)
    
#用户登录
def do_login(s,user,name,addr):
    if (name in user) or name == "管理员":
        s.sendto("该用户已存在".encode(),addr)
        return
    s.sendto(b'OK',addr)
    #通知所有人
    msg = "\n欢迎 %s 进入聊天室"%name
    for i in user:
        s.sendto(msg.encode(),user[i])
    #插入user
    user[name] = addr 

def do_chat(s,user,name,data):
    msg = "\n{} 说: {}".format(name,data)
    for i in user:
        if i != name:
            s.sendto(msg.encode(),user[i])

def do_quit(s,user,name):
    msg = "\n%s 离开了聊天室"%name 
    for i in user:
        if i == name:
            s.sendto(b'EXIT',user[i])
        else:
            s.sendto(msg.encode(),user[i])
    del user[name] #删除离开的用户


#接收客户端请求并处理
def do_parent(s):
    # 用于存储用户 {'Alex':('127.0.0.1',8888)}
    user = {}
    while True:
        msg,addr = s.recvfrom(1024)
        msgList = msg.decode().split(' ')
        if msgList[0] == 'L':
            do_login(s,user,msgList[1],addr)
        elif msgList[0] == 'C':
            # "C Levi [I miss you]"
            data = ' '.join(msgList[2:])
            do_chat(s,user,msgList[1],data)
        elif msgList[0] == 'Q':
            do_quit(s,user,msgList[1])    


# 创建套接字,网络连接,创建父子进程
def main():
    #server address
    ADDR = ('0.0.0.0',8888)
    #创建套接字
    s = socket(AF_INET,SOCK_DGRAM)
    s.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
    s.bind(ADDR)

    #创建父子进程
    pid = os.fork()
    if pid < 0:
        sys.exit("创建进程失败")
    elif pid == 0:
        do_child(s,ADDR)
    else:
        do_parent(s)

if __name__ == "__main__":
    main() 

客户端:

from socket import * 
import sys,os 

def login(s,ADDR):
    while True:
        name = input("请输入用户名:")
        msg = "L " + name
        s.sendto(msg.encode(),ADDR)
        #接收登录结果
        data,addr = s.recvfrom(1024)
        if data.decode() == 'OK':
            print("@进入聊天室@")
            return name
        else:
            print(data.decode())
#发送消息
def do_child(s,name,addr):
    while True:
        text = input("发言(quit退出):")
        #退出
        if text.strip() == "quit":
            msg = "Q " + name 
            s.sendto(msg.encode(),addr)
            sys.exit("退出聊天室")

        msg = "C %s %s"%(name,text)
        s.sendto(msg.encode(),addr)

#接收消息
def do_parent(s):
    while True:
        msg,addr = s.recvfrom(1024)
        if msg.decode() == 'EXIT':
            sys.exit(0)
        print(msg.decode()+"\n发言(quit退出):",end="")

#main控制套接字的创建
def main():
    if len(sys.argv) < 3:
        print("argv is error")
        return
    HOST = sys.argv[1]
    PORT = int(sys.argv[2])
    ADDR = (HOST,PORT)

    s = socket(AF_INET,SOCK_DGRAM)
    
    name = login(s,ADDR)
    if name:
        pid = os.fork()
        if pid < 0:
            sys.exit("创建子进程失败")
        elif pid == 0:
            do_child(s,name,ADDR)
        else:
            do_parent(s)
    else:
        return 


if __name__ == "__main__":
    main()

multiprocessing 模块创建进程

  1. 需要将要做的事情进行封装成函数
  2. 使用multiprocessing 提供的类Process创建进程对象
  3. 通过进程对象和Process初始化进程进行进程的设置,绑定函数
  4. 启动进程,会自动执行绑定的函数
  5. 完成进程的回收

创建进程对象

Process()

  • 功能:创建进程对象
  • 参数:target:要绑定的函数
               name:给进程起的,名称(默认process-1)
               args:元组 用来给target函数位置传参
               kwargs:字典 用来给target函数键值传参

p.start()

  • 功能:启动进程 自动运行terget绑定函数,此时进程被创建

p.join([timeout])

  • 功能:阻塞等待子进程退出
  • 参数:超时时间
  • 使用multiprocessing创建进程子进程同样复制父进程的全部内存空间,之后有自己独立空间,执行上互不干扰
  • 子进程也是有自己特有的PID等资源
  • 如果不使用join回收可能会产生僵尸进程
  • 使用multiprocessing创建子进程,一般父进程功能就是创建子进程回收子进程,所有事件交给子进程完成
    import multiprocessing as mp 
    from time import sleep 
    import os
    
    a = 1
    
    def fun():
        sleep(2)
        print("子进程事件",os.getpid())
        global a
        a = 10000
        print("a = ",a)
    
    #创建进程对象
    p = mp.Process(target = fun)
    
    #启动进程
    p.start() 
    
    sleep(3)
    print("这是父进程")
    
    #回收进程
    p.join()
    print("parent a:",a)
    
    # while True:
    #     pass

作业:

  1. 梳理,聊天室代码
  2. 对进程概念和创建过程巩固
  3. 加深对http协议的理解
  4. 使用父子进程复制一个文件,分别复制文件的上半部分和下半部分到一个新的文件中,以字节区分
    解析:
    import os 
    from multiprocessing import Process 
    from time import sleep
    
    #获取文件的大小
    size = os.path.getsize("./timg.jpeg")
    # f = open("timg.jpeg",'rb')
    #复制前半部分
    def copy1(img):
        f = open(img,'rb')
        n = size // 2
        fw = open('1.jpeg','wb')
    
        while True:
            if n < 1024:
                data = f.read(n)
                fw.write(data)
                break
            data = f.read(1024)
            fw.write(data)
            n -= 1024
        f.close()
        fw.close()
    
    #复制后半部分
    def copy2(img):
        f = open(img,'rb')
        fw = open('2.jpeg','wb')
        f.seek(size // 2,0)
        while True:
            data = f.read(1024)
            if not data:
                break 
            fw.write(data)
        fw.close()
        f.close()
    
    p1 = Process(target = copy1,args = ('timg.jpeg',))
    p2 = Process(target = copy2,args = ('timg.jpeg',))
    p1.start()
    p2.start()
    p1.join()
    p2.join()
    


 

猜你喜欢

转载自blog.csdn.net/qq_42584444/article/details/81557285