网络编程之并发

操作系统:
第一代计算机:真空管,穿孔卡片
没有操作系统,直接操作硬件
优点:程序员独享计算机资源,可以及时调试bug
缺点:计算机资源浪费,当程序员出现bug调试时,计算机闲置
第二代计算机:晶体管,批处理系统
针对第一代计算机的缺点:
1.将多人的程序攒成一波输入
2.顺序计算,串行
3.将多个程序的运行结果攒成一波输出
优点:提高了计算机利用率
缺点:程序员需等待一波输入的结果出来后才能知道自己的程序运行结果,出bug了也无法及时的调试
需要人控制
串行,按顺序执行
第三代计算机:集成电路芯片和多道程序
多道技术:当只有一个cpu时
多道技术:①操作系统将程序读入内存,内存中可放入多个程序
②cpu在程序中切换执行,提高了cpu利用率
cpu在两种情况下会切换:①当程序遇到IO时②当执行一个程序过长时

多道技术中的多道指的是多个程序,多道技术的实现是为了解决多个程序竞争或者说共享同一个资源(比如cpu)的有序调度问题,解决方式即多路复用,多路复用分为时间上的复用和空间上的复用。
空间上的复用:多个程序占用的内存空间是相互分离的
时间上的复用:cpu在执行一个程序过长或程序遇到IO操作时会切换

进程,程序
程序:一堆文件
进程:运行中的程序
并发:cpu在多个程序间切换执行,给人一种多个程序在同时执行的感觉,单核
并行:多个程序同时执行,叫并行
子进程:
当执行程序时遇到程序无法执行的任务需开启另一个进程,则被开启的进程称为子进程
创建子进程的两种方法:

#第一种
from multiprocessing import Process

def task():
    print("子进程run")

if __name__ == "__main__":
    p = Process(target = task)
    p.start()
    print("主进程run")
#第二种
from multiprocessing import Process
# 创建进程的第二种方式  继承Process  覆盖run方法
# 在子进程启动以后会自动执行run方法
# 其优势是 可以自定义 进程的属性和行为 来完成一些额外任务 例如下载
# class MyProcess(Process):
#
#     def __init__(self,url):
#         self.url = url
#         super().__init__()
#
#     # 子类中的方法  只有run会被自动执行
#     def run(self):
#         print("下载文件...." , self.url)
#         print(" run run run!")
#
#     def task(self):
#         pass
#
# def task():
#     print(123)

# if __name__ == '__main__':
#     p = MyProcess("www.baidu.com/xx.mp4")
#     p.start()   

windows 和unix系统创建的子进程:

window:子进程和主进程有独立的内存空间,子进程会copy主进程中的代码,如果我们不将创建子进程的操作放入if__name__==“main”下的话,子进程导入主进程时也会执行创建子进程的操作,导致无限循环的创建子进程

unix:
在UNIX中,子进程的初始地址空间是父进程的一个副本,提示:子进程和父进程是可以有只读的共享内存区的。但是对于windows系统来说,从一开始父进程与子进程的地址空间就是不同的。

进程的三种状态:就绪,阻塞,运行
由于CPU的切换机制,我们想提高我们程序的执行效率的话,需要尽量减少IO操作,使我们的程序多处于就绪态

子进程的join方法
join方法,当我们需要在所有子进程执行完毕后再执行主进程时,就需要使用join方法了

#不加join
from multiprocessing import Process
import time
def task(n):
    print(n)
    time.sleep(6-n)
    print("子进程%s运行完毕"%n)

if __name__ == '__main__':
    for i in range(5):
        p = Process(target=task, args=(i,))
        p.start()
    print("父进程运行完毕")
#执行结果
#父进程运行完毕
# 0
# 1
# 2
# 3
# 4
# 子进程4运行完毕
# 子进程3运行完毕
# 子进程2运行完毕
# 子进程1运行完毕
# 子进程0运行完毕
#p.start()是向操作系统发起请求开启子进程,开启子进程是需要时间的,然后会进行下一次循环,(经测验我的计算机循环20次,子进程会起来),此时循环5次后,子进程并未起来,故此时先打印父进程执行完毕

#在for循环内使用join
def task(n):
    print(n)
    time.sleep(6-n)
    print("子进程%s运行完毕"%n)


if __name__ == '__main__':
    for i in range(5):
        p = Process(target=task, args=(i,))
        p.start()
        p.join()
    print("父进程运行完毕")
#执行结果
# 0
# 子进程0运行完毕
# 1
# 子进程1运行完毕
# 2
# 子进程2运行完毕
# 3
# 子进程3运行完毕
# 4
# 子进程4运行完毕
# 父进程运行完毕
#对每一个子进程都提高它的优先级,相当于串行了

#3.在for循环外进行join
def task(n):
    print(n)
    time.sleep(6-n)
    print("子进程%s运行完毕"%n)
if __name__ == '__main__':
    for i in range(5):
        p = Process(target=task, args=(i,))
        p.start()
    p.join()
    print("父进程运行完毕")
#执行结果
#0
# 1
# 2
# 3
# 4
# 子进程4运行完毕
# 父进程运行完毕
# 子进程3运行完毕
# 子进程2运行完毕
# 子进程1运行完毕
# 子进程0运行完毕
#相当于对最后一个子进程提升优先级,它执行完成后,父进程就会执行

#4.使用for循环join
from multiprocessing import Process
import time
def task(n):
    print(n)
    time.sleep(6-n)
    print("子进程%s运行完毕"%n)
if __name__ == '__main__':
    ps = []
    for i in range(5):
        p = Process(target=task, args=(i,))
        p.start() 
        ps.append(p)
    for p in ps:
        p.join()
    print("父进程运行完毕")
#执行结果
# 0
# 1
# 2
# 3
# 4
# 子进程4运行完毕
# 子进程3运行完毕
# 子进程2运行完毕
# 子进程1运行完毕
# 子进程0运行完毕
# 父进程运行完毕
#循环增加子循环的优先级,确保父进程在所有子进程完成后才会执行,与第二种方法的区别,子进程的启动是并发的,第二种方法就是串行,无并发可言

子进程的其他方法和属性
p1.terminate()#关闭进程,不会立即关闭,所以is_alive立刻查看的结果可能还是存活
print(p1.is_alive()) #判断子进程是否在运行中,结果为True

僵尸进程和孤儿进程
孤儿进程:
正常运行程序时,主进程会在所有子进程结束后才会停止运行,
因为父进程需要清除子进程的残留信息,
那为什么子进程运行结束后不自行清除残留信息,
因为父进程需要在其未终止的任何时刻都能拿到子进程的pid和属性信息,所以不能让子进程自己消除,而是父进程来消除(使用join方法会清除子进程信息)
那么如果子进程未运行结束,父进程被干死了,此时的子进程称为孤儿进程,操作系统会接手,清除孤儿进程的残留信息,故孤儿进程是无害的
僵尸进程:
父进程没有关闭,子进程运行结束后,会残留pid和属性信息,占用系统内存,称为僵尸进程,当僵尸进程过多时会导致过多占用pid和系统内存,僵尸进程是有害的

猜你喜欢

转载自www.cnblogs.com/robert-zhou/p/10197498.html