网络编程_并发编程


一.操作系统理论

  • 计算机的硬件组成

    • 主板 固化(寄存器, 是直接和cpu进行交互的一个硬件)
    • cpu 中央处理器: 计算 (数字计算和逻辑计算) 和控制 (控制所有硬件协调工作)
    • 存储 硬盘, 内存
    • 输入设备 键盘, 鼠标, 话筒
    • 输出设备 显示器, 音箱, 打印机
      • 早期的计算机是以计算为核心的
      • 现在的计算机是以存储为核心的
    • 第一代计算机: 电子管计算机, 及其耗电, 体积庞大, 散热量特别高
    • 第二代计算机: 晶体管计算机
    • 第三代计算机: 白色大头计算机, 集成电路计算机. 一个板子固化几十到上百个小硬件
    • 第四代计算机: 大型集成电路计算机, 一个板子可以达到固化十万个硬件
    • 第五代计算机: 甚大型集成电路计算机
  • 计算机的操作系统

    • 操作系统是一个软件, 是一个能直接操纵硬件的一个软件,微软研发的windows系统
    • 人工时代: 穿孔卡带, 每个人都能在一段时间内独享计算机所有资源
    • 脱机时代: 完全将人和及其隔离开来
    • 单道批处理系统: 内存中允许存放一道作业
    • 多道批处理系统: 内存中允许存放多道作业
    • 分时系统: 将cpu和执行划分时间片, 每个程序以时间片为单位去执行
    • 实时系统: 一般比较少见, 主要用于军事和工业生产上
    • 操作系统的目的: 让用户用起来更加轻松, 高可用, 低耦合
  • 语言的发展史

    • 计算机识别的二进制, 机器语言, 由0 和 1 组成代码
      • 汇编语言, add ,n,m
      • 高级语言: 面向过程(C) 面向对象语言(C++, java, python, php)
    • os(操作系统)
      • dos系统 纯编程系统 单用户单任务
      • windows系统 单用户多任务(早期的windows)
      • unix系统 多用户多任务
  • 操作系统的作用

    • 封装所有硬件接口, 让各种用户使用电脑更加轻松
    • 是对计算机内所有资源进行合理的调度和分配

二.进程

  • 进程的理论
    • .sh shell脚本文件
    • .out linux系统中的可执行文件
    • .bat 批处理脚本文件
    • .lib 库文件
    • .dll 库文件
    • .exe 可执行文件, 双击能直接运行的文件
  • 进程
    • 是指正在执行的程序,是程序执行过程中的一次 指令, 数据集等的集合
    • 也可以叫做程序的一次执行过程
    • 进程是一个动态的概念
  • 进程由三大部分组成: 代码段, 数据段, PCB: 进程管理控制
  • 进程的三大基本状态:
    • 就绪状态: 已经获得运行需要的所有资源, 处理cpu
    • 执行状态: 已经获得了所有的资源包括cpu, 处于正在运行
    • 阻塞状态: 因为各种原因, 进程放弃了cpu, 导致进程无法继续执行,此时进程处于内存中,继续等待获取cpu
  • 进程的一个特殊状态
    • 挂起状态: 是指因为各种原因,进程放弃了cpu, 导致进程无法继续执行,此时进程被踢出内存
  • multiprocessing 内置模块, 是python提供, 主要用于多线程编程

三.多进程

1.名词解释
  • 同步和异步关注的是消息通信机制
  • 并行: 并行是指两者同时执行, 比如有两条车道,在某一时间点, 两条车道上都有车在跑(资源够用,比如三个线程, 四核的CPU) 指两件或多件事在同一时间点执行

  • 并发: 指两件或多件事在同一时间间隔同时执行

    • 区别: 并行是从微观上,也就是在一个精确的时间片刻,有不同的程序在执行, 这就要求必须有多个处理器

      并发是从宏观上,在一个时间段上可以看出是同时执行的, 比如一个服务器同时处理多个session

    • 早起单核CPU时候, 对于进程也是微观上串行(站在cpu角度看), 宏观上并行(站在人的角度看就是同时有很多程序执行)

  • 同步: 所谓同步就是一个任务的完成需要依赖另外一个任务时, 只有等待被依赖的任务完成时,依赖的任务才能算完成,这是一种可靠的任务序列.要么都成功. 要么都失败, 两个任务的状态可以保持一致
  • 异步: 所谓异步是不需要等待依赖的任务完成, 只是通知被依赖的任务要完成什么工作, 依赖的任务也立即执行, 只要自己完成了整个任务就算完成了, 至于被依赖的任务最重是否真正完成, 依赖它的任务无法确定, 所以它是不可靠任务序列
  • 阻塞: 阻塞是当请求不能满足的时候将进程挂起

  • 非阻塞: 非阻塞是不会阻塞当前进程,非阻塞调用指在不能立刻得到结果之前也会立刻返回,同时该函数不会阻塞当前线程。

  • 阻塞和非阻塞针对的是进程或线程
2.进程的两种开启方法

(1) p = Process(target = func, args (,))

from multiprocessing import Process
import time
import os


def func(i):
    time.sleep(1)
    print('这里是%s儿子进程%s,父进程是%s' % (i, os.getpid(), os.getppid()))


if __name__ == '__main__':

    for i in range(2):
        p = Process(target=func, args=(i,))
        p.start()
    print('这里是父亲进程%s' % (os.getpid()))

(2) 自定义类, 继承Process类

# 自定义类继承Process
class MyProcess(Process):
    def __init__(self):
        self.name = name
        super(MyProcess, self).__init__(name=self.name)  # 实现父类的__init__方法

    def run(self):
        print('这是以继承的方式开启子进程%s' % self.name)


if __name__ == '__main__':
    name = 'alex'
    p1 = MyProcess()
    p1.start()  # 是指解释器告诉操作系统, 去帮我开启一个进程, 属于就绪状态
    # p1.run()  # 告诉操作系统,现在马上执行这个子进程, 属于执行状态
3.如何开启多个不同的子进程
#  开启多个子进程
def func(i):
    time.sleep(1)
    print('这是第%s个子进程,进程id是%s' % (i, os.getpid()))


if __name__ == '__main__':
    for i in range(10):
        p = Process(target=func, args=(i,))
        p.start()
    print('父进程的ID是%s' % os.getpid())
4.进程的常用方法(start. join,terminate, is_alive)
from multiprocessing import Process
import time


def func():
    for i in range(10):
        time.sleep(0.1)
        print('儿子在这里')


if __name__ == '__main__':
    p = Process(target=func)
    p.start()
    p.join()  # 当程序执行到这一句话时, 主进程开始阻塞. 等待子进程执行完
    for i in range(10):
        time.sleep(0.1)
        print('爸爸在这里')

#  开启一个正常的子进程, 父进程会等待子进程结束后, 父进程也就是程序才结束
#  p.join() 是主进程等待子进程执行完.  现象:  主进程执行到这句话时,主进程阻塞主,等待子进程执行


def func():
    time.sleep(1)
    print(123)


if __name__ == '__main__':
    p = Process(target=func)
    p.start()
    p.terminate()  #  杀死p进程, 让解释器告诉操作系统, 请杀死p进程
    print('子进程是否活着?', p.is_alive())
    print('父进程id%s'% os.getpid())
    time.sleep(0.01)
    print('子进程是否活着?', p.is_alive())
    #  返回一个bool值, 如果返回True. 代表进程还活着, 如果返回False, 代表子进程死了

#  p.is_alive() 判断p进程是否还活着
#  p.terminate() 杀死p进程
  • 如何把父进程和子进程之间的关系变为同步或者异步?
    • 父进程执行join, 就会变成同步, 不执行join. 父进程和子进程的关系就是异步的关系
    • join必须放在start() 后边
5.多个子进程
from multiprocessing import Process
import time
import random


def func(i):
    print('我是%s' % i)


if __name__ == '__main__':
    l = []
    addr = ['上海的', '河南的', '北京的']
    for i in addr:
        p = Process(target=func, args=(i,))
        p.start()
        l.append(p)
    [p.join() for p in l]  # 列表推导式
    time.sleep(1)
    print('我选%s' % (random.choice(addr)))
6.进程的常用属性
  • p.name = 给p进程一个名字
  • p.pid 返回p进程的pid
  • p.daemon = True 将p进程蛇者为守护进程,(True为守护进程, False 为普通进程)
    • 守护进程的两个特点:
      • 守护进程会随着父进程的结束而结束
      • 守护进程不能再创建子进程

四.队列,管道,回调函数

1.生产者消费者模型
  • 主要是为了解耦
  • 借助队列来实现生产者消费者模型
    • 栈: 先进后出(First In Last Out 简称 FILO)
    • 队列: 先进先出(First In First Out 简称FIFO)
import queue # 不能进行多进程之间的数据传输
(1) from multiprocessing import Queue  借助Queue来解决生产者消费者模型
    队列是安全的
    q = Queue(num) 实例化
    num:  队列的最大长度
    q.get()  # 阻塞等待获取数据,如果有数据直接获取, 如果没有数据,阻塞等待
    q.put()  # 阻塞, 如果可以继续往队列中放数据,就直接放, 不能放就直接阻塞等待

    q.get_nowait()  # 不阻塞,如果有数据直接获取, 没有数据直接就报错
    q.put_nowait()  # 不阻塞,如果可以继续往队列中放数据, 就直接方放, 不能放就报错
(2) from multiprocessing import joinableQueue # 可连接队列
    joinableQueue 是继承Queue,
    并且joinableQueue多了两个方法
    q.join()  # 用于生产者 等待q.task_done的返回结果, 通过返回结果,生产者就能获取当前消费者消费了多少数据
    q.task_done()  # 用于消费者, 是指每消费队列中一个数据, 九个join返回一个标识
  • 队列实现生产者消费者模型
    from multiprocessing import Process, Queue
    import time
    
    

def consumer(q, name, color):
while 1:
info = q.get()
if info:
print(‘%s %s 拿走了%s \033[0m’ % (color, name, info))
else:
break

def producer(q, product):
for i in range(20):
q.put(product + ‘的娃娃%s’ % str(i))

if name == ‘main‘:
q = Queue(10)
p_con1 = Process(target=producer, args=(q, ‘贝贝’))
p_con2 = Process(target=producer, args=(q, ‘吉吉’))
p_con3 = Process(target=producer, args=(q, ‘果果’))
p_pro1 = Process(target=consumer, args=(q, ‘alex’, ‘\033[31m’))
p_pro2 = Process(target=consumer, args=(q, ‘wusir’, ‘\033[32m’))
l = [p_con1, p_con2, p_con3, p_pro1, p_pro2]
[p.start() for p in l]
# 父进程如何感知生产者子进程不再生产数据了?
p_con1.join()
p_con2.join()
p_con3.join()
q.put(None) # 几个消费者就要接受几个结束标识
q.put(None)




- joinableQueue新模块

rom multiprocessing import Process, JoinableQueue, Queue

def consumer(q, name, color):
while 1:
info = q.get()
print(‘%s %s 拿走了%s’ % (color, name, info))
q.task_done()

def producer(q, product):
for i in range(20):
q.put(product + ‘的娃娃%s号 \033[0m’ % str(i))
q.join()

if name == ‘main‘:
q = JoinableQueue(10)
p_con = Process(target=consumer, args=(q, ‘alex’, ‘\033[31m’))
p_pro = Process(target=producer, args=(q, ‘果果’))
p_con.daemon = True
p_con.start()
p_pro.start()
p_pro.join() # 主进程等待生产者进程结束
# 程序有3个进程,主进程和生产者进程和消费者进程。 当主进程执行到35行代码时,主进程会等待生产进程结束
# 而生产进程中(第26行)会等待消费者进程把所有数据消费完,生产者进程才结束。
# 现在的状态就是 主进程等待生产者进程结束,生产者进程等待消费者消费完所有数据
# 所以,把消费者设置为守护进程。 当主进程执行完,就代表生产进程已经结束,也就代表消费者进程已经把队列中数据消费完
# 此时,主进程一旦结束,守护进程也就是消费者进程也就跟着结束。 整个程序也就能正常结束了。


- Manger

from multiprocessing import Process, Manager

def func(num):
num[0] -= 1
print(‘子进程找中的num值是’, num)

if name == ‘main‘:
m = Manager()
num = m.list([1,2,3])
p = Process(target=func, args=(num,))
p.start()
p.join()
print(‘父进程中的num值是’, num)


- 管道使用

单进程管道的使用

from multiprocessing import Pipe

con1, con2 = Pipe()

con1.send(‘123’)
print(con2.recv())
con2.send(‘abd’)
print(con1.recv())

多进程管道的使用

from multiprocessing import Process, Pipe

def func(con):
con1, con2 = con
con1.close()
while 1:
try:

        print(con2.recv())
    except EOFError:
        con2.close()
        break

if name == ‘main‘:
con1, con2 = Pipe()
p = Process(target=func, args=((con1, con2),))
p.start()
con2.close() # 在父进程中, 使用con1 和 子进程通信, 所以不需要con2
for i in range(10): # 生产数据
con1.send(i) # 给子进程con2发送数据
con1.close() # 生产完数据, 关闭父进程这一端的管道


- 进程池和多进程效率对比

from multiprocessing import Pool, Process
import os
import time

def func(num):
num += 1
print(num)

if name == ‘main‘:
p = Pool(os.cpu_count() + 1)
start = time.time()
p.map(func, [i for i in range(100)])
p.close() # 值不允许向进程池中添加任务
p.join() # 等待进程池中所有进程执行完任务
print(‘进程池做任务的效率’, time.time() - start)

start = time.time()
p_l = []
for i in range(100):
    p1 = Process(target=func, args=(i,))
    p1.start()
    p_l.append(p1)
[p1.join() for p1 in p_l]
print('多进程做任务的效率', time.time() - start)

Author: 二哈君
Reprint policy: All articles in this blog are used except for special statements CC BY 4.0 reprint polocy. If reproduced, please indicate source 二哈君 !
 Previous
前端HTML 前端HTML
一.Web服务本质import socket sk = socket.socket() sk.bind("127.0.0.1" , 8888) sk.listen(5) while 1: conn, ad
2021-03-22 二哈君
Next 
面试总结 面试总结
1.遍历字典for k,v in dic.items() 2.什么叫闭包?函数内部调用函数外部(局部)的变量 3.在调用一个函数中,如何结束本程序?quit() sys.exit() exit() 让函数抛出异常 4.交换机和路由器的区别?
2020-12-01 二哈君
  TOC