1、操作系统

  位于硬件与应用软件之间,本质也是一种软件,由系统内核和系统接口组成

  和进程之间的关系是:

    进程只能由操作系统创建

  和普通软件区别:

    操作系统是真正的控制硬件

    应用程序实际在调用操作系统提供的接口
    主要功能:

    1. 封装了繁琐的硬件操作,为应用程序提供简单的接口

    2.将应用程序对硬件的竞争变成有序的使用

2、多道技术

   为了解决同一时间只有一个应用程序被执行问题

  方法:切换+保存状态

    切换:IO阻塞;执行时间过长;出现一个优先级更高的任务

    保存状态:在任务切换前保存运行状态,以便后期恢复执行

  实现原理:

    空间复用:同一时间在内存中存放多个应用程序

    时间复用:多个进程占用CPU同一时间片在不同的进程中相互切换, 遇到IO操作或占用CPU时间过长时,保存当前进程状态,切换到其他进程

 强调:

    多个进程之间相互独立,并是物理层面的隔离,以保证安全性  

  好处:

    同一时间可以多个应用程序在执行,实现并发,在IO操作比较多时,极大的提高效率

  弊端:

    如果没有IO操作,切换反而会降低效率

  应用程序的执行取决于IO操作,IO操作越多则效率越低

  提高效率: 减小IO操作,避免被操作系统切换

3、进程与程序

   进程:正在运行的程序,是一系列程序执行过程的总称

  程序:一段代码,程序员将自己思维逻辑按照某种编程语言规范编写下来的一堆字符串

  进程是由程序产生的,没有程序就没有进程

4、进程的三种状态

  并行、并发、串行、阻塞

     串行:按照固定顺序执行, 第一行执行完毕后才会执行第二行
          需要注意的是 串行执行的过程中 因为计算操作太多也会导致程序卡住,但是这与io阻塞是不同的
     并发:多进程同时运行,(单核+多道技术)
     并行:多进程同时执行(多核才能实现)
     阻塞:进程的状态。进程执行时遇到IO操作。CPU切换到其他进程

5、PID和PPID

   import os

  os.getpid(), 当前进程id

  os.getppid(),当前进程的父进程id

6、进程的层次结构

  无论是unix还是windows,进程只有一个父进程,不同的是:

    1. 在unix所有的进程,都是以init进程为根,成树形结构。

    2. 在window中,没有进程层次的概念,所有进程的地位相同,在创建进程时,父进程会得到一个特殊令牌(句柄),该句柄可以控制子进程,但父进程可以把该句柄传给其他子进程。

7、python中实现多进程

  7.1 实现的两种方式

     方式1:实例化Process类
    from multiprocessing import Process
    def task(name):
      print(name)
    if __name__ == '__main__':
      p = Process(target=task, args=('sun',))
      p.start()
      print("主")
  方式2:继承Process类
    from multiprocessing import Process
    class MyProcess(Process):
      def __init__(self, name):
        super(MyProcess, self).__init__()
        self.name = name
      def run(self):
        print(self.name)
    if __name__ == '__main__':
      p = MyProcess('sun')
      p.start()
  特别注意:
    1. 在windows下,开启子进程必须在__main__下面,但在Linux环境下就不用, 原因:
      windows开启子进程与linux开启子进程方式不同, Linux,直接将主进程的内存中,完整的copy子进程到自己的空间然后执行。
      windows,如果没有__main__函数,子进程会在运行时拷贝当前主进程中的所有内容,
       这也就意味着当一个新的子进程被创建的时候,该子进程就会复制当前模块,其中也包括这两句:p = MyProcess('sun'), p.start()。
      很显然,这样写法可能形成无限递归式的创建子进程,所以为了避免以上情况,引入if __name__ == '__main__':,
    2. 第二种方式,必须将执行的代码放到run方法中。
    3. 主进程会等待所有子进程结束后在结束

  7.2 进程之间的内存相互隔离

    from multiprocessing import Process
    import time
    x=1000
    def task():
      time.sleep(3)
      global x
      x=0
      print('子进程',x)
    if __name__ == '__main__':
      print('main', x)
      p=Process(target=task)
      p.start()
      p.join() # 让父亲在原地等
      print(x)

   输出结果:

     main 1000
       子进程 0
     1000

  7.3 进程的方法和属性

    start(),向操作系统发送一个开子进程信号
    join(),使主进程等待子进程执行完毕后才会执行,本质是内部修改子进程优先级高于主进程
    terminate(), 向操作系统发送一个终止子进程信号,发完之后继续执行
    is_alive(), 获取进程的存活状态
   
    p.name, 在创建时可以给进程取名字
    p.exitcode, 获取进程的退出码  只有进程完全结束后才能获取到
    p.pid # 获取进程的pid

8、 守护进程

  指的是一个进程守护另一个进程。

  如果把子进程变成守护进程则:  

    1. 守护进程会在主进程结束后就终止(主进程代码执行结束后会等待其他子线程结束后结束)

    2. 守护进程内无法开启子进程。否则抛出异常AssertionError

from multiprocessing import Process
import time def task():
print("subprocess is running.....")
time.sleep(5)
print("subprocess is down....") if __name__ == '__main__':
p = Process(target=task, )
p.daemon = True
print('main going....')
p.start()
time.sleep(1)
print("exit....")

守护进程示

9、进程的安全问题

  问题:多个进程并发去操作同一资源,由于是竞争关系就很容易出现错乱问题

  解决方案:加锁,在访问共享资源前加锁,访问完毕后一定要解锁

  使用工具:互斥锁

  注意:

    不能多次执行acquire,一次acquire对应一次release

    acquire是一个阻塞函数。会一直等到锁被释放后才会继续执行

  和join的区别:加锁之后,并发变串行,和join不同的是,没有规定顺序,谁先抢到谁执行;并且join是整个任务串行,锁仅仅是部分代码串行。

from multiprocessing import Process, Lock
import time def task1(lock):
lock.acquire()
print("task1 is running.....")
time.sleep(1)
print('task1 is over.....')
lock.release() def task2(lock):
lock.acquire()
print("task2 is running.....")
time.sleep(1)
print('task2 is over.....')
lock.release() if __name__ == '__main__':
lock = Lock()
p1 = Process(target=task1, args=(lock,))
p2 = Process(target=task2, args=(lock,)) p1.start()
p2.start() print('main over')
import json
from multiprocessing import Process,Lock
import time def search(name):
with open("data", "rt") as f:
data = json.load(f)
print("%s查看剩余票数:%s"%(name, data['num'])) def get(name):
time.sleep(0.5)
with open('data', 'rt') as f:
data = json.load(f)
if data['num'] > 0:
data['num'] -= 1
with open('data', 'wt') as f:
json.dump(data, f, indent=4)
print("%s成功购买到票"%name)
else:
print("票卖完了,请%s等候"%name) def task(name, lock):
search(name)
lock.acquire()
get(name)
lock.release() if __name__ == '__main__':
users = ['aaa', 'bbb', 'ccc']
lock = Lock()
for user in users:
p = Process(target=task, args=(user, lock))
p.start()

模拟抢票

10、进程之间的通讯IPC

  进程之间内存是相互隔离的无法直接通讯

  四种方式(间接通讯):

    1. 使用一个共享文件,在硬盘创建一个文件。不同进程之间共享这个文件

      优点:交换的数据量几乎没有限制
         缺点:速度慢

    2. 系统开辟一块共享内存 以供进程之间交换数据

      优点:速度快
      缺点:数据量不能太大

    3. 管道

      优点:封装文件的打开,关闭等操作
      缺点:速度慢,并且单向的,编程复杂度较高  

    4.  socket 不仅可以用于与远程计算机中的进程通讯,还可以用于与本地进程通讯 

      基于内存 速度快

from multiprocessing import Process, Manager, Lock

def task(dic):
dic['a'] += 1
print('子进程对a进行加法%s'%dic['a']) if __name__ == '__main__':
m = Manager()
dic = m.dict({'a': 10})
print("计算前a的值%s"%dic['a'])
p = Process(target=task, args=(dic,))
p.start()
p.join()
print("计算后a的值%s"%dic['a']) #  注意:如果有多个进程需要对共享内存操作,需要加上互斥锁,保证数据的安全性

 11、队列

  特点:先进先出

  创建:  from multiprocessing import Queue
         q = Queue(maxsize)  #maxsize表示队列允许的最大项数

  方法:

q.put(block, timeout), 插入数据到队列,block=True,表示队列已满时阻塞等待插入数据,
timeout表示阻塞等带的时间,超时抛出异常。block=False,表示队列已满时,不阻塞等待,直接抛出异常
q.get(block, timeout),取出数据,参数同上
q.get_nowait() = q.get(False)
q.put_nowait() = q.put(False) q.empty(),q为空返回True,该结果不可靠,比如返回True时,又加入了新的数据
q.full(),满,不可靠
q.qsize(),个数,不可靠

方法

 12、生产者消费者模型

  问题:由于生产者和消费者能力不匹配,导致双方可能相互等待,造成了效率降低

  解决方法:

      使用一个容器隔离生产者和消费者,生产者把生产的产品放到容器内,消费者直接从容器内取商品。

     这样两者就不用相互等待,平衡了生产者和消费者之间的能力差异 提高处理效率,降低了双方耦合度,

  实现方式:可以使用队列实现生产者消费者模型

from multiprocessing import Process, Queue
import time
import random def producer(q):
"""生产者生产商品"""
for i in range(1,11):
time.sleep(random.randint(1,3))
produce = "baozi%s"%i
q.put(produce)
print('生产者蒸了一个第%s个包子'%i) def consumer(q):
"""消费者消费商品"""
for i in range(10):
food = q.get()
time.sleep(random.randint(1,3))
print("消费者吃了一个包子:%s"%food) if __name__ == '__main__':
q = Queue()
# 生产者
produce = Process(target=producer, args=(q,))
produce.start()
# 消费者
custom = Process(target=consumer, args=(q,))
custom.start()

代码示例

13、JoinableQueue

  指的是加入Join阻塞的队列。

    与普通队列的区别是,可以等待队列所有数据处理完毕后才执行后面程序

  JoinableQueue除了包含Queue的方法外,还有两个特殊的方法。

  q.task_done(), 用来发送信号,表示q.get()取出的数据已经被处理完毕。如果调用次数大于队列内项目的数量,将引发ValueError异常。

  q.join(), 用来阻塞,只有队列的数据被处理,且每次处理后都调用q.task_done()方法,程序才继续执行。

from multiprocessing import JoinableQueue,Queue

q = JoinableQueue()

q.put()
q.put() print(q.get()) q.task_done() # 告诉容器已经处理完了一个数据
q.task_done() #有几个就要调用几次
# q.join() # 也是一个阻塞函数 一直到队列中的数据被处理完毕 (task_done的调用次数等于队列中的数据数量)
print("处理完成") # print(q.get())
print(q.empty())

示例

from multiprocessing import Process, Queue, JoinableQueue
import time
import random def producer(q):
"""生产者生产商品"""
for i in range(,):
time.sleep(random.randint(,))
produce = "baozi%s"%i
q.put(produce)
print('生产者蒸了第%s个包子'%i) def consumer(q):
"""消费者消费商品"""
while True:
food = q.get()
time.sleep(random.randint(,))
print("消费者吃了一个包子:%s"%food)
q.task_done() if __name__ == '__main__':
q = JoinableQueue()
# 生产者
produce = Process(target=producer, args=(q,))
produce.start()
# 消费者
custom = Process(target=consumer, args=(q,))
custom.daemon = True
custom.start() # 等待生产者生产完包子
produce.join()
# 等待所有包子吃完就结束
q.join() print("main over....")

使用JoinableQueue改进生产者消费者模型

  

python并发编程之多进程基础知识点的更多相关文章

  1. python并发编程之多线程基础知识点

    1.线程理论知识 概念:指的是一条流水线的工作过程的总称,是一个抽象的概念,是CPU基本执行单位. 进程和线程之间的区别: 1. 进程仅仅是一个资源单位,其中包含程序运行所需的资源,而线程就相当于车间 ...

  2. Python并发编程__多进程

    Python并发编程_多进程 multiprocessing模块介绍 python中的多线程无法利用多核优势,如果想要充分地使用多核CPU的资源(os.cpu_count()查看),在python中大 ...

  3. Python进阶(4)_进程与线程 (python并发编程之多进程)

    一.python并发编程之多进程 1.1 multiprocessing模块介绍 由于GIL的存在,python中的多线程其实并不是真正的多线程,如果想要充分地使用多核CPU的资源,在python中大 ...

  4. python并发编程02 /多进程、进程的创建、进程PID、join方法、进程对象属性、守护进程

    python并发编程02 /多进程.进程的创建.进程PID.join方法.进程对象属性.守护进程 目录 python并发编程02 /多进程.进程的创建.进程PID.join方法.进程对象属性.守护进程 ...

  5. Python - 并发编程,多进程,多线程

    传送门 https://blog.csdn.net/jackfrued/article/details/79717727 在此基础上实践和改编某些点 1. 并发编程 实现让程序同时执行多个任务也就是常 ...

  6. python并发编程之多进程(三):共享数据&进程池

    一,共享数据 展望未来,基于消息传递的并发编程是大势所趋 即便是使用线程,推荐做法也是将程序设计为大量独立的线程集合 通过消息队列交换数据.这样极大地减少了对使用锁定和其他同步手段的需求, 还可以扩展 ...

  7. python并发编程之多进程(二):互斥锁(同步锁)&进程其他属性&进程间通信(queue)&生产者消费者模型

    一,互斥锁,同步锁 进程之间数据不共享,但是共享同一套文件系统,所以访问同一个文件,或同一个打印终端,是没有问题的, 竞争带来的结果就是错乱,如何控制,就是加锁处理 part1:多个进程共享同一打印终 ...

  8. 28 python 并发编程之多进程

    一 multiprocessing模块介绍 python中的多线程无法利用多核优势,如果想要充分地使用多核CPU的资源(os.cpu_count()查看),在python中大部分情况需要使用多进程.P ...

  9. 二 python并发编程之多进程-重点

    一 multiprocessing模块介绍 python中的多线程无法利用多核优势,如果想要充分地使用多核CPU的资源(os.cpu_count()查看),在python中大部分情况需要使用多进程.P ...

随机推荐

  1. SpringCloud Alibaba-nacos注册中心

    什么是 Nacos?(https://nacos.io) Nacos 致力于帮助您发现.配置和管理微服务.Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现.服务配置.服务元数据及流量 ...

  2. Java开发笔记(八十四)文件与目录的管理

    程序除了处理内存中的数据结构,还要操作磁盘上的各类文件,这里的磁盘是个统称,泛指可以持久保留数据的存储介质,包括但不限于:插在软驱中的软盘.固定在机箱中的硬盘.插在光驱中的光盘.插在USB接口上的U盘 ...

  3. 初学Shiro

    Shiro Shiro是什么? Apache Shiro是Java的一个安全(权限)框架. Shiro可以非常容易的开发出足够好的应用,其不仅可以用在JavaSE环境下,也可以用者JavaEE环境下 ...

  4. Tkinter小技巧:如何为窗口右上角的‘x’添加一个自定义的响应函数

    不废话,直接上代码 import tkinter as tk from tkinter import messagebox main_window = tk.Tk() main_window.geom ...

  5. Android ble蓝牙使用注意

    以下均为自己在Android ble开发项目中遇到的问题 1.尽量不要在BluetoothGattCallback里面的回调函数中执行读写通知操作,最多一个,因为例如在onServicesDiscov ...

  6. Flask技术问题汇总

    1:Flask 使用 request对象代理了当前请求的上下文.这么做什么好处和坏处? 好处:flask封装了C端发起request对象,这样就可以使用上下文临时把某些对象变为全局可访问:如果不封装, ...

  7. 阿里云OCR图片转换成文字识别调用

    using Newtonsoft.Json; using System; using System.Collections.Generic; using System.Drawing; using S ...

  8. Win10 Ubuntu子系统运行32bit Linux原生程序

    本文主要描述的是:解决  Win10 Ubuntu子系统中运行  32bit Linux原生程序 报错  Exec format error . 问题来源于  在 Win10 Ubuntu子系统中运行 ...

  9. gradle下载及配置

    windows安装 1.下载地址:http://services.gradle.org/distributions/ 2.下载**-bin.zip,解压即可 配置环境变量:gradle_home:D: ...

  10. 使用docker快速搭建nginx+php环境

    在朋友的强烈推荐下,走上了docker之路.经过了繁琐的docker环境安装,看了下镜像/容器的简单使用,开始进行nginx+php环境的搭建,本文记录一下在安装过程中的笔记. 原文地址:代码汇个人博 ...