并发

一、操作系统发展史

  1.穿孔卡片

  2.磁带

  3.批处理

  本质:都是想提高CPU的利用率

二、多道技术

  1.空间上的复用

    多个程序公用一台硬件设备

  2.时间上的复用(洗衣,做饭,烧水)

    cpu在多个程序之间来回切换着执行程序

    cpu什么时候会切换:

      (1)程序占用cpu时间过长,操作系统会剥夺该程序的cpu执行权限(降低了程序的执行效率)

      (2)程序遇到IO操作,操作系统会剥夺该程序的cpu执行权限(提高了利用率,并且也不影响程序的执行效率)

    并发:看起来像同时运行的就可以

    并行:真正意义上的同时执行,单核的计算机能不能实现并行,但是可以实现并发

三、进程理论

  1.程序:一堆代码

   进程:正在运行的程序

  2.同步异步:表示的是任务的提交方式

    同步:任务提交之后,原地等待的任务的执行并拿到返回结果才走,期间不做任何事(程序层面的表现就是卡住了)

    异步:任务提交之后,不再原地等待,而是继续执行下一行代码(结果是要的,但是是用其他方式获取)

  3.阻塞非阻塞:表示的程序的运行状态

    阻塞:阻塞态

    非阻塞:就绪态,运行态

    强调:同步异步,阻塞非阻塞是两对概念,不能混为一谈

四、创建进程的两种方式

  1.创建进程就是在内存中重新开辟一块内存空间,

    将允许产生的代码丢进去

    一个进程对应在内存就是一块独立的内存空间

  2.进程与进程之间数据是隔离的,无法直接交互,

    但是可以通过某些技术实现间接交互

  3.windows创建进程会将代码以模块的方式,从上往下执行一遍

   linux会直接将代码完完整整的拷贝一份

   windows创建进程一定要在if __name__ == '__main__':代码块内创建,否则报错

  4.方法一:

from multiprocessing import Process
import time


def test(name):
    print('%s is running'%name)
    time.sleep(3)
    print('%s is over'%name)

if __name__ == '__main__':
    p = Process(target=test,args=('egon',))  # 创建一个进程对象
    p.start()  # 告诉操作系统帮你创建一个进程
    print('')

  方式二:

from multiprocessing import Process
import time

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

    def run(self):
        print('%s is running' % self.name)
        time.sleep(3)
        print('%s is over' % self.name)

if __name__ == '__main__':
    p = MyProcess('egon')
    p.start()
    print('')

五、进程方法join

  start()作用仅仅是告诉操作系统帮你创建一个进程,至于这个进程什么时候创建由操作系统随机决定

  join()作用主进程代码等待子进程运行结束

from multiprocessing import Process
import time

def test(name,i):
    print('%s is running'%name)
    time.sleep(i)
    print('%s is over'%name)
if __name__ == '__main__':
    p_list = []
    # for i in range(3):
    #     p = Process(target=test,args=('进程%s'%i,i))
    #     p.start()
    #     p_list.append(p)
    # for p in p_list:
    #     p.join()
    p = Process(target=test,args=('egon',1))
    p1 = Process(target=test,args=('kevin',2))
    p2 = Process(target=test,args=('jason',3))
    start_time = time.time()
    p.start()  # 仅仅是告诉操作系统帮你创建一个进程 至于这个进程什么时候创  操作系统随机决定
    p1.start()
    p2.start()
    p2.join()
    p.join()
    p1.join()

    # 主进程代码等待子进程运行结束 才继续运行
    # p.join()  # 主进程代码等待子进程运行结束
    print('')
    print(time.time() - start_time)

六、进程对象及其他方法

  查看进程id号

    current_process().pid  >>>  current_process()返回的是Process()

    os.getpid()  os.getppid()

  terminate()作用是杀死当前进程,其实是告诉操作系统帮你杀死一个进程

  is_alive()作用是判断进程是否存活

from multiprocessing import Process,current_process
import os
import time


def test(name):
    # print('%s is running'%name,current_process().pid)
    print('%s is running'%name,'子进程%s'%os.getpid(),'父进程%s'%os.getppid())
    time.sleep(3)
    print('%s is over'%name)


if __name__ == '__main__':
    p = Process(target=test,args=('egon',))
    p.start()
    p.terminate()  # 杀死当前进程  其实是告诉操作系统帮你杀死一个进程
    time.sleep(0.1)
    print(p.is_alive())  # 判断进程是否存活
    # print('主',current_process().pid)
    print('',os.getpid(),'主主进程:%s'%os.getppid())

七、进程间数据互相隔离

  如何验证  开一个子进程修改主进程里面的数据,根本无法修改

from multiprocessing import Process
import time


money = 100

def test():
    global money
    money = 99999999


if __name__ == '__main__':
    p = Process(target=test)
    p.start()
    p.join()
    print(money)

八、守护进程

  主进程一旦运行完毕,子进程立即结束运行(一起死!)

  p.daemon = True作用是将进程设置为守护进程,这一句话必须放在start语句之前,否则报错

from multiprocessing import Process
import time


def test(name):
    print('%s总管正常活着'%name)
    time.sleep(3)
    print('%s总管正常死亡'%name)


if __name__ == '__main__':
    p = Process(target=test,args=('egon',))
    p.daemon = True  # 将该进程设置为守护进程   这一句话必须放在start语句之前 否则报错
    p.start()
    time.sleep(0.1)
    print('皇帝jason寿正终寝')

九、互斥锁

  当多个进程操作同一份数据的时候,会造成数据的错乱

    这个时候必须加锁处理,将并发变成串行

    虽然降低了效率,但是提高了数据的安全

  注意:

    1.锁不要轻易使用,容易造成死锁现象

    2.只在处理数据的部分加锁,不要在全局加锁

  锁必须在主进程中产生,交给子进程去使用

  mutex.ecquire()  # 抢锁  一把锁不能同时被多个人使用,没有抢到的人就一直等待锁的释放

  buy(1)

  mutex.release()  # 释放锁

  抢票模拟

from multiprocessing import Process,Lock
import time
import json

# 查票
def search(i):
    with open('data','r',encoding='utf-8') as f:
        data = f.read()
    t_d = json.loads(data)
    print('用户%s查询余票为:%s'%(i,t_d.get('ticket')))

# 买票
def buy(i):
    with open('data','r',encoding='utf-8') as f:
        data = f.read()
    t_d = json.loads(data)
    time.sleep(1)
    if t_d.get('ticket') > 0:
        # 票数减一
        t_d['ticket'] -= 1
        # 更新票数
        with open('data','w',encoding='utf-8') as f:
            json.dump(t_d,f)
        print('用户%s抢票成功'%i)
    else:
        print('没票了')


def run(i,mutex):
    search(i)
    mutex.acquire()  # 抢锁  只要有人抢到了锁 其他人必须等待该人释放锁
    buy(i)
    mutex.release()  # 释放锁


if __name__ == '__main__':
    mutex = Lock()  # 生成了一把锁
    for i in range(10):
        p = Process(target=run,args=(i,mutex))
        p.start()

十、僵尸进程与孤儿进程

  子进程结束之后不会立即释放pid等资源信息

  主进程释放子进程资源的两种情况:

    主进程正常死亡

    join方法

  1.所有的进程都会步入僵尸进程

    父进程回收子进程资源的两种方式

      (1)join方法

      (2)父进程正常死亡

  2.孤儿进程

    子进程没死,父进程意外死亡

    针对linux会有儿童福利院(init),如果父进程意外死亡他所创建的子进程都会被福利收养

原文地址:https://www.cnblogs.com/yljbky/p/11328493.html