并发编程(一)

并发编程(上)

操作系统发展史

操作系统的发展史

穿孔卡片:

一个计算机机房,一次只能被一个卡片使用.

缺点:CPU利用率最低.

联机批处理系统:

支持多用户去使用一个计算机机房.

脱机批处理系统:

高速磁盘:提高文件的读取速度

优点:提高CPU的利用率

进程基础

进程是对正在运行程序的一个抽象.

  1. 操作系统的作用

    隐藏丑陋复杂的硬件接口,提供良好的抽象接口

    管理,调度进度,并且将多个进程对硬件的竞争变得有序

  2. 多道技术(基于单核情况下研究):

    单道:多个使用使用CPU时是串行.

    多道技术:

    空间上的复用-->一个CPU可以提供给多个用户去使用.

    时间上的复用-->切换 + 保存状态

    IO操作:input();print();time.sleep(3)

    1 若CPU遇到IO操作,会立即将当前执行程序CPU使用权断开

    2 若一个程序使用CPU的时间过长,会立即将当前执行程序CPU使用权断开.

    特点:程序的执行率降低

进程调度
  1. 先来先服务调度算法

    a,b程序,若A先来,先占用CPU;缺点是程序A先使用,程序B必须等待程序A使用CPU结束后才能使用.

  2. 短作业优先调度算法

    a,b程序,谁的用时短,先优先调度使用CPU;缺点是若程序A使用时间长,有N个程序使用时间短.必须等待所有短的程序结束后才能使用.

  3. 时间片轮转法

    CPU执行的时间一秒钟,加载N个程序,将要一秒等分成N个时间片.

  4. 多级反馈队列

    将执行优先分为多层级别.

    一级;优先级最高

    二级;优先级第二,依次类推

并发与并行

==并行==

并行是指两者同时执行,比如赛跑,两个人都在不停的往前跑;(资源够用,比如三个线程,四核CPU)

==并发==

并发是指资源有限的情况下,两者交替轮流使用资源,比如一段路(单核CPU资源)同时只能过一个人,A走一段后,让给B,B用完继续给A,交替使用,目的是提高效率.

同步异步阻塞非阻塞
  1. 同步

    若有两个任务需要提交,在提交第一个任务时,必须等待该任务执行结束后,才能继续提交并执行第二个任务

  2. 异步

    若两个任务需要提交,在提交第一个任务时,不需要原地等待,立即可以提交并执行第二个任务

  3. 阻塞

    阻塞态,遇到IO一定会阻塞(凡是遇到IO操作的进程,都会进入阻塞态.若IO结束,必须重新进入就绪态.

  4. 非阻塞

    就绪态(所有进程创建时都会进入就绪态,准备调度)

    运行态(调度后的进程,进入运行态)

创建进程的两种方式

==UNIX和Windows创建进程==

  1. 在UNIX中该系统调用是:fork,fork会创建一个与父进程一模一样的副本,二者有相同的映像,同样的环境字符串和同样的打开文件(在shell解释器进程中,执行一个命令就会创建一个子进程)
  2. 在Windows中该系统调用是: createprocess, createprocess既处理进程的创建.也负责八正确的程序装入新进程.

关于创建子进程,UNIX和Windows:

  1. 相同的是: 进程创建后,父进程和子进程有各自不同的地址空间,(多道技术要求物理层面实现进程之间内存的隔离),任何一个进程在其他地址空间中的修改都不会影响到另外一个进程.
  2. 不同的是:在UNIX中,子进程的初始地址空间是父进程的一个副本,提示:子进程和父进程是可以有只读的共享内存区的,但是对于Windows系统来说,从一开始父进程与子进程的地址空间就是不同的.

进程的结束:1.正常退出;2.出错退出;3.严重错误;4.被其他进程杀死

开启多进程(multiprocess.process)

multiprocess模块

仔细来说,multiprocess不是一个模块,而是python中一个操作,管理进程的包.由于提供的子模块非常的多,为了方便归类记忆,可以分为四个部分:创建进程部分,进程同步部分,进程池部分,进程之间数据共享.

  1. multiprocess.process模块

    process模块是一个创建进程的模块,借助这个模块,就可以完成进程的创建.

  2. process模块

    process([group [,target [,name [,args [,kwargs]]]]])

    由上面类实例化得到的对象,表示一个子进程的任务(尚未启动)

    强调:

    需要使用关键字的方式来指定参数

    args指定的为传给target函数的位置参数,是一个元组形式,必须有逗号

    参数介绍:

    1. group参数未使用,值始终为none
    1. target表示调用对象,即子进程要执行的任务
    1. args表示第哦啊用对象的位置参数元组,args=(q,2,'egon').
    2. kwargs表示调用对象的字典,kwargs={'name':'egon','age':19}
    3. name为子进程的名称

    方法介绍:

    1. p.start:启动进程,并调用该子进程的p.run()
    2. p.run():进程启动时运行的方法,正是它去调用target指定的函数,我们自定义类的类中一定要实现该方法
    3. p.terminate():强制终止进程P,不会进行任何清理操作,如果P还保存了一个锁那么也将不会被释放,进而导致死锁
    4. p.is_alive:如果P仍然运行,返回true。
    5. 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. p.name:进程的名称

    3. p.pid:进程的pid。

    在Windows中使用process注意事项

    在Windows操作系统中,由于没有fork(Linux操作系统中创建进程的机制),在创建子进程的时候会自己动import启动它的这个文件,而在import的时候又执行了整个文件。因此如果将process()直接写在文件中就会无线递归创建子进程报错,所以必须把创建子进程的部分使用if__name___=='__main__'判断保护起来,import的时候,就不会递归运行了。

  3. 使用process模块创建进程

    在一个python进程中开启子进程start方法和并发效果

  4. 守护进程

    会随着主进程的结束而结束

    主进程创建守护进程

    1. 守护进程会在主进程代码执行结束后九终止、
    2. 守护进程内无法再开启子进程,否则抛出异常
  5. socket并发实例

    使用多进程实现socket聊天并发送——server端

    使用多进程实现socket聊天并发——client端

  6. 多进程中其他方法

    进程对象的其他方法:terminate和is_alive

    进程对象的其他属性:pid和name

进程同步(multiprocess.lock)

通过学习了异步,让多个任务可以同时在几个进程中并发处理,他们之间的运行没有顺序,一旦开启也不受我们控制。尽管并发编程让我们能更加充分的利用io资源,但是也给我们带来了新的问题:当多个进程使用同一份数据资源的时候,就会引发数据安全或顺序混乱问题。

  1. 多进程抢占输出资源

    import os 
    import time
    import random
    from multiprocessing import process
    
    def wrok(n):
        print('%s: %s is runing' %(n,os.getpid()))
        time,sleep(random.random)
     print('%s: %s is done' %(n,os.getpid()))
    
    if __name__ == '__main__':
        for i in range(3):
            p=process(target=work,args=(i,))
            p.start
  2. 使用锁维护执行顺序

    import os 
    import time 
    import random
    from multiprocess import process,lock
    
    def work(lock,n):
        lock.acquire()
        print('%s: %s runing' %(n,os.getpid()))
        time.sleep(random.random())
        print('%s: %s is done' %(n,os.getpid()))
        lock.release()
    if __name__=='__main__':
        lock=lock()
        for i in range()
         p=process(targs=work,args=(lock,i))
            p.start()

    上述这种情况虽然使用加锁的形式实现了顺序的执行,但是程序又重新变成串行了,这样确实会浪费了时间,却保证了数据的安全。

  3. 使用锁来保证数据安全

    加锁可以保证多个进程修改同一块数据时,同一时间只能有一个任务可以修改,即串行的修改,没错,速度是慢了,但牺牲了速度却保证了数据安全。

    虽然可以使用文件共享数据实现进程间的通信,但问题是:

    1. 效率低(共享数据基于文件,而文件是硬盘上的数据)
    2. 需要自己加锁处理

    因此我们最好找寻一种解决方案能够兼顾:

    1. 效率高(多个进程共享一块内存的数据)
    2. 帮我们处理好加锁问题。这就是mutiprocessing模块为我们提供的基于消息的ipc通信机制:队列和管道。

    队列和管道都是将数据存放于内存中,队列又是基于(管道+锁)实现的,可以让我们从复杂的锁问题中解脱出来,我们应该尽量避免使用共享数据,尽可能使用消息传递和队列,避免处理复杂的同步和锁问题,而且在进程数目增多时,往往可以获得更好的可获展性。

posted @ 2019-10-21 19:44  Mr-Allen  阅读(83)  评论(0编辑  收藏  举报