第1章 Python基础 Day1 基本语法
1.1 编程语言介绍与分类
1.2 Python介绍、发展趋势
1.3 Python环境安装
1.4 开发你的第一个Python程序
1.5 选择最好用的PyCharm IDE
1.6 变量
1.7 注释
1.8 基本数据类型
1.9 读取用户指令
1.10 格式化打印
1.11 运算符
1.12 流程控制之if...else
1.13 流程控制之while循环
1.14 本章练习题&作业
第2章 Python基础 Day2 数据类型和文件操作
2.1 上章补充-变量的创建过程
2.2 上章补充-身份运算和None
2.3 细讲数据类型-列表
2.4 细讲数据类型-元组
2.5 细讲数据类型-字符串
2.6 细讲数据类型-字典
2.7 细讲数据类型-集合
2.8 秒懂二进制
2.9 字符编码之文字是如何显示的
2.10 秒懂十六进制
2.11 hash是个什么东西
2.12 用Python操作文件
2.13 本章练习题&作业
第3章 Python 基础 Day3 函数编程
3.1 上章补充-Bytes类型
3.2 上章补充-字符编码的转换
3.3 上章补充-深浅copy
3.4 函数来了
3.5 函数返回值与作用域
3.6 嵌套&匿名&高阶函数
3.7 函数的递归
3.8 内置函数
3.9 名称空间
3.10 闭包是个什么东西?
3.11 函数进阶-装饰器
3.12 列表生成式
3.13 生成器
3.14 迭代器
3.15 练习题&作业
第4章 Python基础 Day4 常用模块
4.1 模块介绍与导入
4.2 第3方开源模块的安装使用
4.3 系统调用os模块
4.4 系统调用sys模块
4.5 time & datetime模块
4.6 random随机模块
4.7 序列化pickle&json模块
4.8 hashlib 加密
4.9 文件copy模块shutil
4.10 正则表达式re模块
4.11 软件开发目录设计规范
4.12 包&跨模块代码调用
4.13 练习题&作业
第5章 Python核心编程 Day5 面向对象编程
5.1 面向对象来了
5.2 面向对象语法
5.3 对象间的交互、组合
5.4 三大特性之-继承
5.5 三大特性之-封装
5.6 三大特性之-多态
5.7 作业&练习题
第6章 Python核心编程 Day6 面向对象编程进阶
6.1 类方法、静态方法
6.2 属性方法property
6.3 神奇的反射
6.4 类的双下线方法
6.5 用type动态创建一个类
6.6 isinstance\issubclass
6.7 异常处理
6.8 作业&练习题
第7章 核心编程 Day7 Socket网络编程
7.1 C/S架构介绍
7.2 TCP/IP 各层详解
7.3 Socket介绍
7.4 Socket代码实例
7.5 粘包现象与解决方案
7.6 通过socket发送文件
7.7 本章总结
第8章 核心编程 Day8 并发编程
8.1 操作系统介绍
8.2 并发编程多进程之进程理论
8.3 并发编程多进程之开启进程的两种方式
8.4 并发编程多进程之join方法
8.5 并发编程多进程之守护进程
8.6 并发编程多进程之互斥锁
8.7 并发编程多进程之队列
8.8 并发编程多进程之生产者消费者模型
8.9 并发编程多线程之线程理论
8.10 并发编程多线程之开启线程的两种方式
8.11 并发编程多线程之多线程与多进程的区别
8.12 并发编程多线程之Thread对象的其他属性或方法
8.13 并发编程多线程之守护线程
8.14 并发编程多线程之GIL全局解释器锁
8.15 并发编程多线程之死锁现象与递归锁
8.16 并发编程多线程之信号量,Event,定时器
8.17 并发编程多线程之线程queue
8.18 并发编程多线程之进程池与线程池
8.19 并发编程之协程-协程介绍
8.20 并发编程之协程-greenlet模块
8.21 并发编程之协程-gevent模块
8.22 并发编程IO模型-IO模型介绍
8.23 并发编程IO模型-阻塞IO
8.24 并发编程IO模型-非阻塞IO
8.25 并发编程IO模型-多路复用IO
8.26 并发编程IO模型-异步IO
8.27 并发编程IO模型-IO模型比较分析
8.28 并发编程IO模型-selectors模块
8.29 本章小结
第9章 Mysql数据库开发
9.1 初识数据库-数据库管理软件的由来
9.2 初识数据库-数据库概述
9.3 初识数据库-mysql安装与基本管理
9.4 初识数据库-初识sql语句
9.5 库操作-库的增删改查
9.6 表操作-存储引擎介绍
9.7 表操作-表的增删改查
9.8 表操作-数据类型
9.9 表操作-数值类型
9.10 表操作-日期类型
9.11 表操作-字符串类型
9.12 表操作-枚举类型与集合类型
9.13 表操作-完整性约束
第7章 爬虫开发-requests模块学习
7.1 requests模块初始
7.2 requests案例实战
第21章 爬虫开发-爬虫基础简介
21.1 爬虫初识&价值探讨
21.2 爬虫合法性探究
21.3 爬虫初识深入
21.4 http&https协议
第9章 爬虫开发-数据解析
9.1 数据解析概述
9.2 数据解析---正则表达式
9.3 数据解析---bs4解析
9.4 数据解析---xpath解析
第10章 爬虫开发-验证码识别
10.1 验证码识别
10.2 验证码实战
第11章 requests模块高级操作
11.1 模拟登陆
11.2 requests模块的cookie处理
11.3 requests模块的代理IP操作
第13章 高性能异步爬虫
13.1 高性能异步爬虫---线程and线程池
13.2 高性能异步爬虫---异步协程
13.3 高性能异步爬虫---多任务异步协程
13.4 高性能异步爬虫---aiohttp
第25章 scrapy框架使用
25.1 scrapy简介
25.2 scrapy的数据持久化存储
25.3 scrapy基于Spider类的全站数据爬取
25.4 请求传参
25.5 scrapy图片数据爬取
25.6 scrapy中间件
25.7 scrapy中selenium的应用
第12章 动态渲染页面爬取
12.1 图片懒加载
12.2 selenium模块基本使用
12.3 基于selenium的爬虫案例
12.4 Pyppeteer模块使用
Python入门到精通
Java放弃之路
前端开发
Linux从小白到大神
并发编程多进程之互斥锁
阅读量: 1806
# 本节重点 - 了解互斥锁的概念 本节时长需控制在15分钟内 ## 一 互斥锁 进程之间数据不共享,但是共享同一套文件系统,所以访问同一个文件,或同一个打印终端,是没有问题的,而共享带来的是竞争,竞争带来的结果就是错乱,如下 ```python #并发运行,效率高,但竞争同一打印终端,带来了打印错乱 from multiprocessing import Process import os,time def work(): print('%s is running' %os.getpid()) time.sleep(2) print('%s is done' %os.getpid()) if __name__ == '__main__': for i in range(3): p=Process(target=work) p.start() ``` 如何控制,就是加锁处理。而互斥锁的意思就是互相排斥,如果把多个进程比喻为多个人,互斥锁的工作原理就是多个人都要去争抢同一个资源:卫生间,一个人抢到卫生间后上一把锁,其他人都要等着,等到这个完成任务后释放锁,其他人才有可能有一个抢到......所以互斥锁的原理,就是把并发改成穿行,降低了效率,但保证了数据安全不错乱 ```python #由并发变成了串行,牺牲了运行效率,但避免了竞争 from multiprocessing import Process,Lock import os,time def work(lock): lock.acquire() #加锁 print('%s is running' %os.getpid()) time.sleep(2) print('%s is done' %os.getpid()) lock.release() #释放锁 if __name__ == '__main__': lock=Lock() for i in range(3): p=Process(target=work,args=(lock,)) p.start() ``` ## 二 模拟抢票练习 多个进程共享同一文件,我们可以把文件当数据库,用多个进程模拟多个人执行抢票任务 ```python #文件db.txt的内容为:{"count":1} #注意一定要用双引号,不然json无法识别 from multiprocessing import Process import time,json def search(name): dic=json.load(open('db.txt')) time.sleep(1) print('\033[43m%s 查到剩余票数%s\033[0m' %(name,dic['count'])) def get(name): dic=json.load(open('db.txt')) time.sleep(1) #模拟读数据的网络延迟 if dic['count'] >0: dic['count']-=1 time.sleep(1) #模拟写数据的网络延迟 json.dump(dic,open('db.txt','w')) print('\033[46m%s 购票成功\033[0m' %name) def task(name): search(name) get(name) if __name__ == '__main__': for i in range(10): #模拟并发10个客户端抢票 name='<路人%s>' %i p=Process(target=task,args=(name,)) p.start() ``` 并发运行,效率高,但竞争写同一文件,数据写入错乱,只有一张票,卖成功给了10个人 ```python <路人0> 查到剩余票数1 <路人1> 查到剩余票数1 <路人2> 查到剩余票数1 <路人3> 查到剩余票数1 <路人4> 查到剩余票数1 <路人5> 查到剩余票数1 <路人6> 查到剩余票数1 <路人7> 查到剩余票数1 <路人8> 查到剩余票数1 <路人9> 查到剩余票数1 <路人0> 购票成功 <路人4> 购票成功 <路人1> 购票成功 <路人5> 购票成功 <路人3> 购票成功 <路人7> 购票成功 <路人2> 购票成功 <路人6> 购票成功 <路人8> 购票成功 <路人9> 购票成功 ``` 加锁处理:购票行为由并发变成了串行,牺牲了运行效率,但保证了数据安全 ```python #把文件db.txt的内容重置为:{"count":1} from multiprocessing import Process,Lock import time,json def search(name): dic=json.load(open('db.txt')) time.sleep(1) print('\033[43m%s 查到剩余票数%s\033[0m' %(name,dic['count'])) def get(name): dic=json.load(open('db.txt')) time.sleep(1) #模拟读数据的网络延迟 if dic['count'] >0: dic['count']-=1 time.sleep(1) #模拟写数据的网络延迟 json.dump(dic,open('db.txt','w')) print('\033[46m%s 购票成功\033[0m' %name) def task(name,lock): search(name) with lock: #相当于lock.acquire(),执行完自代码块自动执行lock.release() get(name) if __name__ == '__main__': lock=Lock() for i in range(10): #模拟并发10个客户端抢票 name='<路人%s>' %i p=Process(target=task,args=(name,lock)) p.start() ``` 执行结果 ```python <路人0> 查到剩余票数1 <路人1> 查到剩余票数1 <路人2> 查到剩余票数1 <路人3> 查到剩余票数1 <路人4> 查到剩余票数1 <路人5> 查到剩余票数1 <路人6> 查到剩余票数1 <路人7> 查到剩余票数1 <路人8> 查到剩余票数1 <路人9> 查到剩余票数1 <路人0> 购票成功 ``` ## 三 互斥锁与join 使用join可以将并发变成串行,互斥锁的原理也是将并发变成穿行,那我们直接使用join就可以了啊,为何还要互斥锁,说到这里我赶紧试了一下 #把文件db.txt的内容重置为:{"count":1} ```python from multiprocessing import Process,Lock import time,json def search(name): dic=json.load(open('db.txt')) print('\033[43m%s 查到剩余票数%s\033[0m' %(name,dic['count'])) def get(name): dic=json.load(open('db.txt')) time.sleep(1) #模拟读数据的网络延迟 if dic['count'] >0: dic['count']-=1 time.sleep(1) #模拟写数据的网络延迟 json.dump(dic,open('db.txt','w')) print('\033[46m%s 购票成功\033[0m' %name) def task(name,): search(name) get(name) if __name__ == '__main__': for i in range(10): name='<路人%s>' %i p=Process(target=task,args=(name,)) p.start() p.join() ``` 执行结果 ```python <路人0> 查到剩余票数1 <路人0> 购票成功 <路人1> 查到剩余票数0 <路人2> 查到剩余票数0 <路人3> 查到剩余票数0 <路人4> 查到剩余票数0 <路人5> 查到剩余票数0 <路人6> 查到剩余票数0 <路人7> 查到剩余票数0 <路人8> 查到剩余票数0 <路人9> 查到剩余票数0 ``` 发现使用join将并发改成穿行,确实能保证数据安全,但问题是连查票操作也变成只能一个一个人去查了,很明显大家查票时应该是并发地去查询而无需考虑数据准确与否,此时join与互斥锁的区别就显而易见了,join是将一个任务整体串行,而互斥锁的好处则是可以将一个任务中的某一段代码串行,比如只让task函数中的get任务串行 ```python def task(name,): search(name) # 并发执行 lock.acquire() get(name) #串行执行 lock.release() ``` ## 四 总结 加锁可以保证多个进程修改同一块数据时,同一时间只能有一个任务可以进行修改,即串行地修改,没错,速度是慢了,但牺牲了速度却保证了数据安全。 虽然可以用文件共享数据实现进程间通信,但问题是: 1、效率低(共享数据基于文件,而文件是硬盘上的数据) 2、需要自己加锁处理 因此我们最好找寻一种解决方案能够兼顾: 1、效率高(多个进程共享一块内存的数据) 2、帮我们处理好锁问题。 这就是mutiprocessing模块为我们提供的基于消息的IPC通信机制:队列和管道。 队列和管道都是将数据存放于内存中,而队列又是基于(管道+锁)实现的,可以让我们从复杂的锁问题中解脱出来,因而队列才是进程间通信的最佳选择。 我们应该尽量避免使用共享数据,尽可能使用消息传递和队列,避免处理复杂的同步和锁问题,而且在进程数目增多时,往往可以获得更好的可获展性。
并发编程多进程之守护进程
并发编程多进程之队列