进程VS线程
多进程和多线程是实现多任务最常用的两种方式.实现多任务,通常会设计Master-Worker模式,Master负责分配任务,worker负责执行任务.
多任务环境下,就是一个Master,多个Worker.
如果用多进程实现Master-Worker,那主线程就是Master,其他线程就是Worker.
如果用多线程实现Master-Worker,那主线程是Master,其他线程就是Worker.
- 多进程的优势:稳定性高,一个子进程崩溃了,也不会影响其他进程.(主进程崩溃了,其他进程也就挂掉了,由于主进程主要负责分配任务,所以崩溃的几率很低).
- 多进程的缺点:创建进程的代价大,在Windows下创建进程开销巨大.操作系统同时运行的进程数也是有限的,如果有上千个进程在运行,操作系统调度都有问题.
- 多线程的优势就是比多进程快一点,这个一点可以忽略不计.
- 最大的缺点就是,一旦一个线程挂掉了,可能会导致其他线程的崩溃.因为所有线程共享进程的内存.
在Windows系统下多线程的效率要比多进程要高.
线程切换
无论是多进程还是多线程,只要数量达到一定上限,效率都会下降.如果有几千个任务同时进行,操作系统就把主要的精力花费在了切换任务上,没有多少时间执行任务.
计算密集型和IO密集型
是否考虑多任务,还要考虑任务的类型.可以把任务分为计算密集型和IO密集型 .
- 计算密集型任务的特点是要进行大量的计算,消耗CPU资源,靠的是CPU的运算能力,虽然可以用多任务完成,但任务越多,花费在切换任务的时间就越多,CPU的执行效率就会下降.要想有效的利用CPU,在此类型的多任务下,同时进行的数量应等于CPU的核心数.还有,代码的运行效率也很重要,推荐用c语言编写计算密集型任务.
- IO密集型,主要涉及到网络、磁盘IO任务,这类任务的特点就是占用CPU资源很少,主要都在等待IO操作的完成,(因为IO速度低于CPU和内存的速度).对于此类型任务,任务越多,CPU效率越高,也有上限值.由于IO密集型任务把大部分时间花费在IO上,在CPU的时间很少,所以,对于此类任务,最合适的语言就是开发效率最高(代码量最少)的语言,首选脚本语言.
异步IO
由于CPU和IO之间存在巨大的速度差异,一个任务执行的大部分时间都在等待IO操作,单进程单线程模型会导致别的任务无法并行执行,因此,才需要多进程或多线程模型来支持多任务并发执行.
充分利用操作系统提供的异步IO支持,就可以用单进程单线程模型来执行多任务,这种叫事件驱动模型
对于Python,单线程的异步编程模型称为协程,有了它,就可以基于事件驱动编写高效的多任务程序.
分布式进程
Thread和Process中,优选Process,因为Process更稳定,而且还可以分布到多台机器上,而Thread只能分布到同一台机器的多个CPU上.
Python的multiprocessing模块不但支持多进程,managers子模块还支持把多进程分布到多台机器上,一个服务进程可以作为调度者,将任务分布到其他多个线程中 ,依靠网络通信.
managers模块封装很好,不必了解网络通信的细节,就可以编写分布式多进程程序.
现有一个通过Queue通信的多进程程序在同一台机器上运行,由于, 处理任务的进程任务越来越繁重,想把发送任务的进程和处理任务的进程分布到两台机器上.
分布式进程的方法就是,通过managers模块把Queue通过网络暴露出去,就可以让其他机器的进程访问Queue了.
服务进程,负责启动Queue,把Queue注册到网络上,然后向Queue写入任务:
|
|
运行期间有出现错误:PermissionError: [WinError 5] 拒绝访问.
更改后的task_master.py的代码,运行正常:
|
|
在一台机器上写多进程程序时,创建的Queue可以直接拿来用,但在分布式多进程环境下,添加任务到Queue不能对原始的task_queue进行操作,必须通过manager.get_task_queue()获得的Queue接口添加.
任务进程,负责处理任务:
|
|
如果运行task_worker.py期间出现ConnectionRefusedError: [WinError 10061]由于目标计算机积极拒绝,请把你的真实的IP地址替换掉127.0.0.1
当启动服务进程task_master.py文件时的运行结果:
|
|
然后再运行任务进程take_worker.py文件,结果:
|
|
然后,服务进程take_master.py就会跟着输出:
|
|
queue对象存储在task_master.py进程中:
Queue能通过网络访问,是通过QueueManager实现的,它管理的不只是一个Queue,要给每个Queue的网络调用接口起个名字,比如:get_task_queue.
authkey,是为了保证两台机器正常通信,不被其他机器恶意干扰.就像一个暗号,如果两边的暗号对不上,那就连接不上了.
Python的分布式进程接口简单,封装良好,适合需要把繁重任务分布到多台机器的环境下.Queue的作用是用来传递任务和接收结果,每个任务的描述数据量要尽量小.
正则表达式
正则表达式是一种用来匹配字符串的工具.设计思想是用一种描述性语言给字符串定义一个规则,凡是符合规则的字符串,就可以认为匹配.
字符集合 | 说明 |
---|---|
\w | 可以匹配任何一个字母或者数字或者下划线 |
\W | W大写,可以匹配任何一个字母或者数字或者下划线以外的字符 |
\s | 可以匹配空格、制表符、换页符等空白字符的其中任意一个 |
\S | S大写,可以匹配任何一个空白字符以外的字符 |
\d | 可以匹配任何一个 0~9 数字字符 |
\D | D大写,可以匹配任何一个非数字字符 |
[ ] | 用来自定义能够匹配 ‘多种字符’ 的表达式。要匹配中括号,请使用 “[“ 和 “]“ |
{ } | 修饰匹配次数的符号。要匹配大括号,请使用 “{“ 和 “}“ |
. | 匹配除了换行符(\n)以外的任意一个字符。要匹配小数点本身,请使用 “.“ |
^ | 匹配输入字符串的开始位置。 |
$ | 匹配输入字符串的结尾位置。 |
+ | 修饰匹配次数为至少 1 次。 |
( ) | 标记一个子表达式的开始和结束位置。 |
* | 修饰匹配次数为 0 次或任意次。 |
python字符串本身也用\转义,使用Python的r前缀,就不用考虑转义了.
|
|
match()方法,如果匹配成功,就返回一个Match对象,如果不匹配,返回None.
切分字符串
单个空格分割:
|
|
多个空格,符号,切割:
|
|
分组
可以用()来进行分组
|
|
^(\d{3})-(\d{3,8})$ 定义了两个组,可以直接分出,区号和电话号码.可以用group()方法取出分组,group(0)代表要匹配的本身的字符串,group(1)就是第一个子字符串,以此类推.
贪婪匹配
正则表达式默认的是贪婪匹配,就是匹配尽可能多的字符.
|
|
^(\d+)(0*)$ 的第一个分组\d+把所有的0都匹配了,后面的0*只匹配了空字符.
加个?来使用非贪婪匹配
|
|
re模块
python提供的模块,包含所有正则表达式的功能.
一个email的匹配:(不一定准确)
|
|