并发编程,并发就是从主观上看起来运行多个进程,是在用单核CUP经行处理是使用。并行与之相比,就是客观在同一时间段,处理不同程序。一个CPU只能在一个时间点做一件事情,但是多核计算机可以完成并行。并发编程与计算机硬件的特性有很大关系。
计算机的发展史,从早期的使用穿孔卡片,到后来的磁带,待现在的磁盘以及读取速度更快的固态硬盘。这些存储设备的一代代更新就是解决CPU利用率,用更快的存储设备与CPU一起工作。存储设备设备的更新迭代,但是做数据处理CUP,如果被占用就不能处理其他数据了。最初单道运行在计算机读取数据和打印数据时,计算机CPU仍处在闲置状态。在一个事件与一个时间之间读入和输出时CPU仍处在闲置状态。
多道技术的应用优化了单道技术的不足。当处理事件A时,让磁盘读取时间B的程序,等待事件A在CPU中运行结束,运行事件B的程序。切换保存状态技术在其中得以运用,使计算机在时间上的复用和空间上的复用的方面都得到了提升。切换保存状态的特点是:当一个程序遇到I/O操作时,操作系统剥夺该程序的运行权限。当一个程序长时间占用CPU,操作系统也会剥夺该程序的运行权限,并且保存运行的状态。操作系统剥夺完程序运行权限后有进程调度。进程调度有四种方式,1,先来先运行方式。2,短作业优先调度方式。3,时间片转换方式。4,多级反馈队列。
并发就是基于操作系统进程调度的属性,在运行多个程序时主观上看起来就是在同时运行,实际上是计算机的进程调度做到这种效果。
计算机的进程。进程就是正在运行的程序。进程有三种状态,就绪态,运行态和阻塞态。将要运行进程可以成称之为就绪态,运行进程称之为运行态,当有事件请求,遇到I/O操作时就是处在阻塞态。
同步与异步:表示的是任务的提交方式。在进程的同步是当程序运行时,任务被提交后等待任务结果出来。而进程的异步是当程序运行时,可以去做其他事情。
阻塞与非阻塞态:阻塞就是进程运行的阻塞态,而非阻塞态就是。
计算机可以同时启动多个进程,在python中也可以,启动多个进程的方式有两种,都是利用类的来创建。第一种是python中使用内置的类,第二种,自定义类。
from multiprocessing import Process def task(): print('子进程') if __name__ == '__main__': p = Process(target=task,) p.start() # 告诉操作系统创建子进程的内存空间, print('主进程') 结果: 主进程 子进程
子进程的创建利用multiprocessing模块中的Process类。在创建子进程的过程:首先是在内存中开辟一块新的子进程的内存空间,将产生的代码copy进去。进程的特点是:每个进程都是一个独立的内存空间,且子进程的创建是随机,子进程的创建时与操作系统有关。使用‘if __name__ == '__main__'’,在内存空间类开辟一块子进程空间时,copy的代码会运行一遍,防止无限制创建进程。
# 子进程创建第二种方式 from multiprocessing import Process class MyProcess(Process): def __init__(self): super().__init__() def run(self): print('子进程') if __name__ == '__main__': p = MyProcess() p.start() print('主进程')
在进程创建时,如果需要子进程运行完毕后再运行主进程需要join()方法。
# jion()方法 from multiprocessing import Process class MyProcess(Process): def __init__(self): super().__init__() def run(self): print('子进程') if __name__ == '__main__': p = MyProcess() p.start() p.join() # jion方法 等待子进程结束后运行下一步程序 print('主进程')
僵尸进程与孤儿进程
僵尸进程:就是之一些进程结束后占用的资源(PID等)没有被回收。自由使用join方法和父进程正常死亡后,才会回收子进程占用资源。
孤儿进程:父进程非正常结束,子进程资源没有回收。Windows系统与Linux系统都有‘儿童福利院’,可以回收这些进行资源。
守护进程:
当一个进程结束,守护它的进程也结束。
# 守护进程 from multiprocessing import Process class MyProcess(Process): def __init__(self): super().__init__() def run(self): print('子进程') if __name__ == '__main__': p = MyProcess() p.daemon = True # 设置子进程为守护进程,主进程结束,子进程也立即结束 p.start() print('主进程')
互斥锁
模拟设置一个抢票情景。当有五个人抢票时,同一时间五个人都可以查到票,但是买票是只允许一个人买票,下一个人在买。否则存票的数据库信息错乱。
from multiprocessing import Process, Lock import time, json, random def show_ticket(i): # 查票 with open('a', 'r', encoding='utf-8') as f: json_d = f.read() ticket = json.loads(json_d) f.flush() print(f'用户{i}正在查票,剩余票数:{ticket.get("ticket")}') def buy_ticket(i): # 买票 with open('a', 'r', encoding='utf-8') as f: json_d = f.read() ticket = json.loads(json_d) time.sleep(random.randint(1,5)) # 模拟网络延迟 if ticket.get('ticket') > 0: ticket['ticket'] -= 1 with open('a', 'w', encoding='utf-8') as f: json.dump(ticket, f) f.flush() print(f'用户{i}抢票成功') else: print(f'用户{i}抢票失败') def task(lock,i): show_ticket(i) lock.acquire() # 抢锁 buy_ticket(i) lock.release() # 放锁 if __name__ == '__main__': key = Lock() for i in range(1,6): p = Process(target=task, args=(key,i)) # 开启五个进程,经行抢票 p.start() print('主进程结束') #结果 主进程结束 用户2正在查票,剩余票数:3 用户1正在查票,剩余票数:3 用户3正在查票,剩余票数:3 用户4正在查票,剩余票数:3 用户5正在查票,剩余票数:3 用户2抢票成功 用户1抢票成功 用户3抢票成功 用户4抢票失败 用户5抢票失败
进程之间的通信
进程间的通信又称为IPC机制。利用队列进行数据交互。其中引入队列模块Queue。
''' 进程之间可与互相通信 子进程间, 主进程与子进程间都可以通信 ''' from multiprocessing import Process, Queue def producer(q): # 进程1 q.put('hello') # 在队列中添加元素 q.put('hello, mm') def consumer(q): print(q.get_nowait()) # 获取队列元素,并打印 if __name__ == '__main__': q = Queue() p1 = Process(target=producer,args=(q,) ) c1 = Process(target=consumer, args=(q,)) p1.start() c1.start() print(q.get()) # 主进程获取队列元素,并打印 # 结果 hello hello, mm