并发编程知识的简单整理(一)

20190914

并发编程知识的整理

操作的系统的历史介绍

第一代计算机(1940~1955):真空管和插件板

第二代计算机(1955~1965):晶体管和批处理系统

第三代计算机(1965~1980):集成电路芯片和多道程序设计

**多道技术 **:多道技术中的多道指的是多个程序,多道技术的实现是为了解决多个程序竞争或者说共享同一个资源(比如cpu)的有序调度问题,解决方式即多路复用,多路复用分为时间上的复用和空间上的复用。

空间上的复用:将内存分为几部分,每个部分放入一个程序,这样,同一时间内存中就有了多道程序(共用一个内存条,多个进程互相隔离,物理级别隔离,程序与程序之间互不干扰

时间上的复用:当一个程序在等待I/O时,另一个程序可以使用cpu,如果内存中可以同时存放足够多的作业,则cpu的利用率可以接近100%,类似于我们小学数学所学的统筹方法。(操作系统采用了多道技术后,可以控制进程的切换,或者说进程之间去争抢cpu的执行权限。这种切换不仅会在一个进程遇到 io 时进行,一个进程占用cpu时间过长也会切换,或者说被操作系统夺走cpu的执行权限)共用一个cpu

分时操作系统:
多个联机终端+多道技术

串行:一个程序完完整整的执行完才能运行下一个程序

并发:看起来像在同时运行(单核),实际上也是串行,区别在于它不一定执行完整,中途在不停地切换

并行:同时执行多个程序

程序:躺在硬盘上的文件
进程:一个执行的程序,是一系列资源的总和

进程与程序区别:进程是正在进行的一个过程或者说一个任务,而程序仅仅仅只是一堆代码。

需要强调的是:同一个程序执行两次,那也是两个进程,比如打开暴风影音,虽然都是同一个软件,但是一个可以播放苍井空,一个可以播放饭岛爱。

进程的三个基本状态

进程执行时的间断性,决定了进程可能具有多种状态。事实上,运行中的进程可能具有以下三种基本状态。
就绪状态(Ready):

进程已获得除处理器外的所需资源,等待分配处理器资源;只要分配了处理器进程就可执行。就绪进程可以按多个优先级来划分队列。例如,当一个进程由于时间片用完而进入就绪状态时,排入低优先级队列;当进程由I/O操作完成而进入就绪状态时,排入高优先级队列。
运行状态(Running):
进程占用处理器资源;处于此状态的进程的数目小于等于处理器的数目。在没有其他进程可以执行时(如所有进程都在阻塞状态),通常会自动执行系统的空闲进程。
阻塞状态(Blocked):
由于进程等待某种条件(如I/O操作或进程同步),在条件满足之前无法继续执行。该事件发生前即使把处理器资源分配给该进程,也无法运行

尽量减少阻塞状态可以提升我们程序运行的效率

并发 = 保存状态+切换

开启子进程 申请新的内存空间 把父进程的所有代码完整拷贝一份过去

# from multiprocessing import Process
# def task(x):
#     pass
#
# if __name__ == '__main__':
#     p = Process(target=task,args=(45,))
#     p.start()

第二种开启子进程的方式(不常用)

# from multiprocessing import Process
#
#
# class Xx(Process):
#     def __init__(self,x):
#         super().__init__()
#         self.x = x
#     def run(self):
#         pass
#
#
# p = Xx()
# p.start()

僵尸进程:父进程的子进程结束的时候父进程没有wait()情况下子进程会变成僵尸进程。(父进程一直不死不停造子进程并且不回收僵尸进程有害)

父进程等着所有的子进程结束才会结束。

孤儿进程(无害)

一个父进程退出,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程。孤儿进程将被init进程(进程号为1)所收养,并由init进程对它们完成状态收集工作。

情况1 无害
父进等着子进程都死,回收僵尸进程。

情况2 无害
父进程死了,子进程活着,都要被init进程接管并且回收。

情况3 有害
父进程一直不死,造成了大量僵尸进程。占用了大量的pid号

pid号是有限的。
解决方案:
最直接的办法就是杀死父进程 。

20190915

Process中join方法

join:阻塞住主进程再等待子进程结束,然后再往下执行,

具体代码如下:

from multiprocessing import Process
import time
def foo():
    time.sleep(4)
    print('进程  start ')
    time.sleep(5)
    print('进程  end ')



if __name__ == '__main__':
    p = Process(target=foo)
    p.start() #
    p.join(3) # 阻塞住主进程再等待子进程结束,然后再往下执行,(了解的是:内部会待用wait())
    print('主')

p.join(3):括号内的数字代表阻塞的时间

多个join的情况,代码如下

from multiprocessing import Process
import time
def task(n):
    print('子进程 开始')
    time.sleep(n)
    print('子进程 结束')
if __name__ == '__main__':
    start_time = time.time() # 记录开始时间
    p1 = Process(target=task,args=(2,))
    p2 = Process(target=task,args=(4,))
    p3 = Process(target=task,args=(6,))

    p1.start()
    p2.start()
    p3.start()
    # 换顺序也是一样的也是按照时长最长的那个计算。
    p1.join() # 等待2s
    p2.join() # 等待2s
    p3.join() # 等待2s
    end_time = time.time() # 记录结束时间
    print(end_time-start_time) # 计算时间差
    print('主线程')
    
    
    
    子进程 开始
    子进程 开始
    子进程 开始
    子进程 结束
    子进程 结束
    子进程 结束
    7.566513776779175
    主线程

join串行的情况

from multiprocessing import Process
import time
def task(n):
    print('子进程 开始')
    time.sleep(n)
    print('子进程 结束')
if __name__ == '__main__':
    start_time = time.time() # 记录开始时间
    p1 = Process(target=task,args=(2,))
    p2 = Process(target=task,args=(4,))
    p3 = Process(target=task,args=(6,))

    p1.start()
    p1.join() 
    p2.start()
    p2.join() 
    p3.start()
    p3.join() 
    end_time = time.time() # 记录结束时间
    print(end_time-start_time) # 计算时间差
    print('主线程')

    '''输出:
    
    子进程 开始
    子进程 结束
    子进程 开始
    子进程 结束
    子进程 开始
    子进程 结束
    15.407774925231934
    主线程
    
    '''
    # ps:反而不如不开进程,正常调用三次来的快。

精炼代码:

from multiprocessing import Process
import time
def task(n):
    print('子进程 开始')
    time.sleep(n)
    print('子进程 结束')
if __name__ == '__main__':
    start_time = time.time() # 记录开始时间
    task_list = []
    for i in range(1,4):
        p = Process(target=task,args=(i,))
        p.start()
        task_list.append(p)
    print(task_list) # [<Process(Process-1, started)>, <Process(Process-2, started)>, <Process(Process-3, started)>]
    for i in task_list:
        i.join()
    end_time = time.time() # 记录结束时间
    print(end_time-start_time) # 计算时间差 4.764175891876221
    print('主线程')

pid的用法

在当前进程查看当前进程pid
    os.getpid()
    current_process().pid
在当前进程查看子进程pid
    子进程对象.pid()
在当前进程查看父进程pid
    os.getppid() 
    
    
from multiprocessing import Process,current_process
import time,os

def task():

    print('子进程 start')
    print('在子进程中查看自己的pid',current_process().pid) # 在子进程中查看自己的pid
    print('在子进程中查看父进程的pid',os.getppid()) #
    time.sleep(200)
    print('子进程 end')

if __name__ == '__main__':

    p = Process(target=task)
    p.start()
    print('在主进程查看子进程的pid',p.pid) # 一定要写在 start()之后
    print('主进程的pid',os.getpid())
    print('主进程的父进程pid',os.getppid())
    print('主')

name的用法:

from multiprocessing import Process,current_process

def task():
    print(current_process().name) # 在子进程查看自己的name

if __name__ == '__main__':
    p = Process(target=task) 
    p2 = Process(target=task)
    p3 = Process(target=task,name='rocky') # 已定义name属性为rocky
    p.start()
    p2.start()
    p3.start()
    print(p.name) #Process-1
    print(p2.name) #Process-2
    print(p3.name) #rocky

is_alive用法:

from multiprocessing import Process,current_process
import time
def task():
    print('子进程开始')
    time.sleep(1)
    print('子进程结束')

if __name__ == '__main__':
    p = Process(target=task)
    p.start()
    # p.terminate() #发送一个指令给操作系统但是不会立即结束子进程
    # time.sleep(1)
    print(p.is_alive()) # True 判断子进程代码是否结束
    time.sleep(3)
    print(p.is_alive()) # False

守护进程:

主进程创建守护进程

  其一:守护进程会在主进程代码执行结束后就终止

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

什么情况适合用守护进程?

当主进程代码结束,该子进程再执行无意义的情况可以用守护进程。

具体代码如下

例一:主进程代码运行完,守护进程立即结束

from multiprocessing import Process
import time
def foo():
    print('守护进程开始')
    time.sleep(5)
    print('守护进程开始')


if __name__ == '__main__':
    p1 = Process(target=foo)
    p1.daemon = True # 一定要凡在start之前,表示设置为一个守护进程。
    p1.start()
    print('主进程')
    
    '''输出:
    主进程
    '''
    # 守护进程一旦发现主进程代码运行完,立刻结束,并不会管自己的进程是否运行完

例二 主进程代码运行完后等在子进程运行阶段,线程不会参与守护

from multiprocessing import Process

import time
def foo():
    print('守护进程开始')
    time.sleep(5)
    print('守护进程结束')
def task():
    print('子进程开始')
    time.sleep(3)
    print('子进程开始')

if __name__ == '__main__':
    p1 = Process(target=foo)
    p2 = Process(target=task)
    p1.daemon = True # 一定要凡在start 之前
    p1.start()
    p2.start()
    # p2 = Process(target=task)
    print('主进程')

    '''输出:
    主进程
    子进程开始
    子进程开始
    '''
    #分析 守护进程在运行完主进程最后一行代码就结束,但是主进程并没有结束,主进程在等待子进程运行结束.
posted @ 2019-09-15 14:38  asyouwish  阅读(147)  评论(0编辑  收藏  举报