并发编程(一)

并发编程理论

image

并行

并行是指“并排行走”或“同时实行或实施”。在操作系统中,若干个程序段同时在系统中运行,这些程序的执行在时间上是重叠的,一个程序段的执行尚未结束,另一个程序段的执行已经开始,无论从微观还是宏观,程序都是同时执行的;

问:单核CPU能否实现并行?
答:不能,但是可以实现并发

并发

并发是指:在同一个时间段内,两个或多个程序执行,有时间上的重叠(宏观上是同时,微观上仍是顺序执行);

通俗讲,多个程序只要看起来像同时运行即可;

问:12306可以同一时间支持几个亿的用户买票 问是并行还是并发
答:高并发

以上建立在单核CPU上~

程序和进程

百度百科:进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。在早期面向进程设计的计算机结构中,进程是程序的基本执行实体;在当代面向线程设计的计算机结构中,进程是线程的容器。程序是指令、数据及其组织形式的描述,进程是程序的实体。

从开发角度,程序可以理解为一堆代码,而进程是怎么产生的呢?

进程的产生可以理解为:正在运行的程序,是活的,而程序(一堆代码)放在那是死的,不运行也不会产生什么;

单核情况下的进程调度

1、FCFS:先来先服务,如果先来一个长作业,比如要执行24h的程序,后面只有1s的程序,这样对短作业是不友好的;
2、短作业优先调度算法:相反这样是对长作业不友好;
		    |
    		|
             👇
3、时间片轮转法+多级反馈队列:先分配给新的多个进程相同的时间片段,之后根据进程消耗时间片多少分类执行;

进程三状态图

就绪、运行、阻塞

查看源图像

  • 就绪态:当进程已分配到除CPU以外的所有必要的资源,只要获得处理机便可立即执行,这时的进程状态称为就绪状态。
  • 运行态:当进程已获得处理机,其程序正在处理机上执行,此时的进程状态称为运行状态。
  • 阻塞态:正在执行的进程,由于等待某个事件发生而无法执行时,便放弃处理机而处于阻塞状态。引起进程阻塞的事件可有多种,例如,等待I/O完成、申请缓冲区不能满足、等待信件(信号)等。

程序在运行之后先进入就绪态,直到第一行代码运行的时候进入运行态,如果中间有代码导致程序夯住,就会导致阻塞态;三态会不断切换;

同步、异步

同步:提交完任务之后原地等待任务的返回结果,期间不做任何事(消耗资源)

异步:提交完任务之后不原地等待任务的返回结果,直接去做其他事 ,结果由反馈机制自动提醒(优化)

# 概念剖析
比如你要做三件事:学习、洗衣服、做饭
# 同步
同步做这三件事的过程:学习期间不能干别的事,不学习了才能去洗衣服,洗衣服期间只能等着洗衣机洗完衣服,不能干别的事,然后衣服洗好了,才能去做饭;
# 结果就是这样又耗时耗力

# 异步
异步做着三件事的过程:衣服丢洗衣机里,米放锅里,然后去学习;这样是不是很轻松,也省时间;

阻塞、非阻塞

二者用于描述任务的执行状态

阻塞:是指调用结果返回之前,当前线程会被挂起

比如python中的input方法获取输入,你不输入会一直处于等待状态,也就是阻塞

非阻塞:就是阻塞的反面,执行和运行

同步/异步、阻塞/非阻塞组合

  • 同步阻塞:这种形式效率是最低的;比如你吃饭排队,只能干等,什么都不能干;
  • 异步阻塞:比如你排队吃饭,排到你了让店员喊一声你就好,这期间可以在附近买杯奶茶等(异步),但是不能离开餐厅附件(阻塞);
  • 同步非阻塞:比如吃饭排队,你估计排队时间挺长的,你打开了王者荣耀,你边打游戏还得往前走关注排队情况,这样以来王者荣耀相当于一个程序,排队是一个程序,二者需要不断切换;
  • 异步非阻塞:效率非常高,比如吃饭排队,你可以把排单号交给女朋友,让她排队,你去厕所(异步),拉屎(非阻塞);这样是不是不需要排队也不需要在餐厅旁边等着,这样就是异步非阻塞;

可能栗子不恰当,不要纠结😁

并发编程实操

创建进程

在windows中开设进程类似于导入模块,从上往下执行代码,一定需要在__main__判断语句内执行开设进程的代码;

在linux中是直接将代码完整的复制一份执行,不需要在__main__判断语句内执行

# 代码层面创建进程
方法一:
from multiprocessing import Process
import time
import os


def test_print(name):
    print(os.getpid())  # 获取进程号
    print(os.getppid())  # 获取父进程号
    print('%s正在运行' % name)
    time.sleep(3)
    print('%s已经结束' % name)


if __name__ == '__main__':
    p = Process(target=test_print, args=('Hammer',))  # 生成一个进程对象
    p.start()  # 告诉操作系统开设一个新的进程,异步提交
    print(os.getpid())
    print('主进程')
    
    
方法二: 采用继承的方法
class MyProcess(Process):
    def __init__(self, name):
        super().__init__()
        self.name = name

    def run(self):
        print('%s正在运行' % self.name)
        time.sleep(3)
        print('%s已经结束' % self.name)

if __name__ == '__main__':
    p = MyProcess('Hammerze')
    p.start()
    print('主进程')

进程的join方法

先进行子进程的代码,执行完子进程执行父进程的代码;

相当于改为串行;

from multiprocessing import Process
import time


def test(name, n):
    print('%s is run' % name)
    time.sleep(n)
    print('%s is over' % name)


if __name__ == '__main__':
    p_list = []
    start_time = time.time()
    for i in range(1, 4):
        p = Process(target=test, args=(i, i))
        p.start()
        p_list.append(p)
        p.join()  # 串行  9s+
   #  for p in p_list:     
   #      p.join()
   #  print(time.time() - start_time)

进程间默认无法交互

# 进程间数据是相互隔离的
from multiprocessing import Process

money = 100

def test():
    global money
    money = 999


if __name__ == '__main__':
    p = Process(target=test)
    p.start()
    # 先确保子进程运行完毕了 再打印
    p.join()
    print(money)  # 100,打印的是主进程的,子进程也修改了

对象方法

方法 说明
current_process 查看进程号
os.getpid() 查看进程号
os.getppid() 查看父进程进程号
terminate() 杀死子进程
is_alive() 判断进程是否存活

terminate()、is_alive()方法结合看不出结果,因为操作系统需要反应时间,主进程睡一下(time.sleep)即可;

from multiprocessing import Process
import os
import time

'''
get id ways:
os.getpid()
os.getppid()
'''
# 创建方法
def test(name):
    print(f'{name} is running ! ')
    time.sleep(3)
    print(f'{name} is over!')

if __name__ == '__main__':
    p = Process(target=test,args=('Hammer',))
    p.start()
    p.terminate()  # 杀死子进程
    time.sleep(0.1)
    print(p.is_alive())  # 判断是不是存活

posted @ 2022-01-13 22:08  HammerZe  阅读(124)  评论(0编辑  收藏  举报