并发编程知识点
多道技术
1、并发:看起来像是同时运行,实际,是靠不断切换任务来实现的
2、并行:同时运行多个任务
3、时间复用:利用中间空闲时间做其他事情。比如;做饭、洗衣服、烧水,就可以先洗好米放锅里煮,然后去把衣服丢洗衣机洗,然后再去烧水,这样就不用等饭煮好,再去洗衣服,烧水。
4、空间复用:多个程序共用一套计算机硬件。比如,电脑同时打开微信、QQ
5、实质:切换+保存。每次切换都会保存切换的时候的状态。
6、单道技术(串行):按任务顺序执行
单道跟多道的比较如下:
- 同步:等被依赖任务执行完后,再执行依赖任务。例子:去银行办理业务,排队等待,一直等待银行办理业务情况
- 异步:被依赖任务、依赖任务同时执行。例子:取号排队,这个过程只需取个号,就可以去做其他事情(开一把王者或者吃鸡之类),等待轮到这个号的时候,业务人员会喊你(类似于回调函数)。
- 阻塞:阻塞态
- 非阻塞:就绪态、执行态
- 实质:在内存中申请一块内存空间存放代码,即,一个进程对应一块内存空间。
- 创建进程的两种方式:
注意:在widows系统下,进程的创建必须在main()内,否则,会报错。因为在widows系统下,进程的创建实质:是把代码复制后,放到新的内存空间,如果创建进程的代码不放 在main()内,则会进入不断创建子进程的死循环
# -*-coding:utf-8 -*- #第一种 # from multiprocessing import Process # import time # # # def task(name): # print(f'{name}我来了') # time.sleep(1) # print(f'{name}我走了') # # if __name__ == '__main__': # p=Process(target=task,args=('jk',)) # p.start() # print('主进程') #第二种 继承类方法 from multiprocessing import Process import time class MyProcess(Process): def run(self): print('我来了') time.sleep(1) print('我走了') if __name__ == '__main__': p=MyProcess(name='hello') p.start() print('主')
- join()方法:等待子进程执行完后再执行。
# -*-coding:utf-8 -*- from multiprocessing import Process import time def task(name): print(f'{name}我来了') time.sleep(1) print(f'{name}我走了') if __name__ == '__main__': p=Process(target=task,args=('jk',)) p1 = Process(target=task, args=('python',)) p2 = Process(target=task, args=('java',)) p.start() p1.start() p2.start() p.join() p1.join() p2.join() print('主进程')
#查看进程号: current_process模块: current_process().pid 查看当前进程的PID号 OS模块: os.getpid() 查看当前进程的PID号 os.getppdi() 查看父进程的PID号 #杀死进程: terminate() #判断进程是否存活: is_alive()
定义:子进程死后,不会立刻释放占用的进程号,原因是为了父进程可以查看该进程占用的pid号,运行时间等基本信息
注意:所有进程都会步入僵尸进程。
最后父进程会回收子进程占用的资源。
定义:子进程存活,父进程意外死亡
结局:由操作系统回收子进程的相关资源。
定义:多个进程操作同一份数据,就会出现错乱的问题
解决方法:导入Lock 模块,给进程加上锁,这样就可以解决互斥锁问题
上锁的弊端:让并行变成串行,效率变低
# -*-coding:utf-8 -*- from multiprocessing import Process,Lock import json import time import random def search(i): with open('data', 'r',encoding='utf-8') as f: dic=json.load(f) num=dic.get('picknum') return dic def buy(i): dict=search(i) time.sleep(random.randrange(1,3)) num=dict.get('picknum') if num>1: dict['picknum']=num-1 with open('data','w',encoding='utf-8') as f: json.dump(dict,f) print('用户%d购票成功'%i) else: print('用户%d购票失败'%i) def run(i,suo): #加锁 suo.acquire() buy(i) #释放锁 suo.release() if __name__ == '__main__': suo = Lock() for i in range(1,11): p=Process(target=run,args=(i,suo)) p.start()
定义:执行单位(真正被执行的真实是进程里面的线程,线程指的就是代码执行过程,执行代码所需要使用的资源都找所在的进程索要)
线程总结:
- 个进程内可以开设多个线程,在用一个进程内开设多个线程无需再次申请内存空间
- 开设线程的开销要远远小于进程的开销。同一个进程下的多个线程数据是共享的
。
不将就