进入WEB基础相关吧:
1.socket
socket: 套接字,这个名字很诡异,反正记住就完事了,别去纠结.
a.流式套接字(SOCK_STREAM):
import socket,socket.socket()中默认为流式套接字,对应传输协议为TCP,优点一堆: 面向连接,可靠,无差错,无重复送,并按顺序接收.
缺点就是粘包.
b.数据报套接字(SOCK_DGRAM):
提供一种无连接的服务,不可靠,有差错,失序,有重复,TCP的优点几乎都没有,但是就是没有粘包.
c.原始套接字(SOCK_RAW):
这个就牛X了,多存于概念里面,工作中基本用不上,有兴趣的自行google.
2.socketserver
由socket + 多线程实现...
有大佬总结的好,直接贴链接: https://www.cnblogs.com/sean-yao/p/7836400.html
3.说一下吧(讲给我自己听,可有不服?)
很多学习python不太久的人估计都和我有差不多的困惑,特别是在web基础这一块,一下这个库,一下又是那个库,而且又多又不好记,特别是到了进程线程那一块,各种库的使用很头疼,就连启动的代码命令都不相同,敲代码敲的十分费劲,何况还要和其它库来联合使用...其实这样也是正常情况,这是正常进阶必须的,可以理解成游戏里面的小BOSS,只不过这个BOSS不是说能一次性KO掉的,需要慢慢磨.
web这一块在学习时可以多学学怎么去使用这个库,比如上面的socket,能写出TCP/UDP基础的服务端和客户端构建就成,也就几个send+sendto的事,没必要想着用socket去搭建一个并发量特别爆炸,功能全宇宙第一的服务器(如果你写出来了,当我没说,互联网,一切皆有可能),或者在去了解socket是在python中是以什么样的机制运作之类的(socket已经算是底层的东西了,正常工作中基本上不会刻意使用),这些底层的库,重要的是了解,而不是剖析.当然,这些话是说给像我这样学习python时间不到五年以及没什么计算机基础的人说的.如果哪天你真的需要去从底层开始构建或了解一整个服务器框架,那么到那时再去深究也不迟.
其实说这些在大佬们看来是废话的东西更多的是激励自己,一方面知道这是正常的学习过程,一方面也是一种鼓舞(buff),火箭还要一级一级射才能升天呢,何况学习呢?针对上面的情况,个人建议多准备几个随身携带的本子,一行一行地把自己认为重要的库的常规例子抄下来,隔几天翻翻,绝对有效果.至于手机党,有道云笔记也不错,前提是打开手机的一瞬间,要下定决心去学习,而不是娱乐,毕竟学习3分钟,娱乐3小时.
4.废话讲完,继续拾遗:
进程,线程,协程,python三贱客.
进程:
from multiprocessing import Process, Pool # 进程,进程池
from concurrent.futures import ProcessPoolExecutor # 进程池 concurrent /kənˈkʌrənt/ 并发 futures 前途(future复数)
linux下: os.fork() # 略
Process:
p = Process(target=func, args=(i,))
p.start() # 开启
p.join() # 阻塞
Pool(进程池是可以获取返回值的):
p = Pool(os.cpu_count())
p.apply(func=func, args=(i,)) # 同步提交
p.apply_saync(func=func, args=(i,)) # 异步提交,后面需要跟上 p.close()和p.join()
p.map(task, iterable[ex: range(20)]) # 异步提交map版,优化代码
ProcessPoolExecutor(看一下这个模块从哪里导入的应该就知道是为了解决什么问题: 并发进程池)
详细介绍: http://c.biancheng.net/view/2627.html(进程池和线程池基本一致)
pp = ProcessPoolExecutor(os.cpu_count()) # 这个数量最好比cpu数量多个1~3,看个人信仰
for i in range(max): # 简单循环示例
pp.submit(func, i) # 特别说明,这里传参就不再是上面的元组了,而是支持位置传参(并行: pp.map(xxx))
pp.shutdown() # 关闭进程池,养成良好习惯,随手关池(with上下文管理,了解一下)
进程间通信:
Process: 主推队列(from multiprocess import Queue),get/put/get_nowait/put_nowait完事
想用管道的也可以试试,队列数据安全,管道得加锁(队列基本实现就是 管道+锁).
Pool和ProcessPoolExecutor: 池子里面请用from multiprocess import Manager,然后用Manager().Queue().
线程:
from _thread import start_new_thread
from threading import Thread
from concurrent.futures import ThreadPoolExecutor
start_new_thread:
start_new_thread(function, args, kwargs=None)
ex:
start_new_thread(func, (i,)) # _thread 提供了低级别的,原始的线程以及一个简单的锁,它相比于 threading 模块的功能还是比较有限的
Thread:
t = Thread(target=func, args=(i,))
t.start()
t.join()
ThreadPoolExecutor: 用法和ProcessPoolExecutor一致.
线程间通信: 同样可以用队列,multiprocessing.Queue就可以,简单逻辑的自己加个锁就完事.
守护进程和守护线程:
在相关模块中,看到daemon这个单词,就是守护的意思,一般调用为True就是设置守护进程/线程.
守护进程: 随着父进程的代码结束而结束;(父进程执行结束)
守护线程: 在主线程代码结束之后,还等待了其它子线程执行结束才结束.(主线程执行结束,其它线也得结束)
协程:
简单理解: 反复横跳的一条线程.(这里的反复横跳指任务切换)
1.使用yield的简单实现: 往往只有yield来实现任务切换并不能解决实际问题.
yield的作用: 当一个人任务阻塞时,会立即切到别的任务.(单纯做任务切换依然需要消耗很多时间)
2.gevent模块
你想要的反复横跳功能前辈们已经帮你实现了,尽管拿去用吧.
1 import gevent 2 3 def eat(): 4 print('eating a') 5 gevent.sleep(4) 6 print('eating b') 7 8 def drink(): 9 print('drinking c') 10 gevent.sleep(3) 11 print('drinking d') 12 13 def play(): 14 print('playing e') 15 gevent.sleep(2) 16 print('playing f') 17 18 def cheer(): 19 print('cheering g') 20 gevent.sleep(1) 21 print('cheering h') 22 23 g1 = gevent.spawn(eat) # 自动检测阻塞,遇见阻塞就切换(不遇见就不切) 24 g2 = gevent.spawn(drink) # 但是有些阻塞无法识别,如time.sleep,需要使用gevent.sleep 25 g3 = gevent.spawn(play) # 想要识别,可以在文件开始from gevent import monkey;monkey.patch_all() 26 g4 = gevent.spawn(cheer) # 猴子补丁,但是可能会产生预料之外的结果,慎用 27 g1.join() # 阻塞到g1结束 28 g2.join() # 阻塞到g2结束 29 g3.join() # 阻塞到g3结束 30 g4.join() # 阻塞到g4结束
最后,总个结:
1 定义: 2 进程: 操作系统中的最小资源分配单位; 3 线程: cpu调度的最小单位,切换由操作系统完成; 4 协程: 规避阻塞,进行任务间切换,且由用户来完成切换工作; 5 6 包含关系: 进程中包含线程,每个进程中至少有一个线程;线程中开启协程; 7 8 资源调度: 9 进程: CPU密集计算,充分利用操作系统资源; 10 线程: 高IO, 资源调度无法干预; 11 协程: 高IO, 资源调度可以自己控制,能否抢占更多的资源取决于自身切换策略; 12 13 多核利用和数据安全: 14 进程: 可以利用多核,且数据隔离; 15 线程: 在cpython解释器下无法利用多核(GIL锁),线程之间数据共享,数据不安全; 16 协程: 无法利用多核,数据安全; 17 18 内存开销和操作系统负担: 19 进程: 创建/切换/销毁的时间开销都比较大, 随着开启数量增加,会给操作系统带来负担; 20 线程: 创建/切换/销毁的时间开销比进程都小很多, 随着开启数量增加,会给操作系统带来负担; 21 协程: 多任务之间切换不依赖操作系统, 无论开启多少都不会增加操作系统负担; 22 23 应用场景: 24 进程: 利用多核/高计算型的程序,且启动数量有限制; 25 线程: 一些协程现有模块无法帮助规避IO操作的场景,适合使用多线程(如urllib); 26 协程: 通用场景,利用现有协程模块尽量规避IO操作;
5.网络IO模型
a.阻塞IO: 一旦阻塞无法向下运行;
b.非阻塞IO: 不断询问,不断反馈,直到不阻塞为止;
c.IO多路复用(事件驱动IO): 代理监听,谁不阻塞执行谁.
d.异步IO: 只发出IO指令,并不等待IO结果,然后就去执行其他代码.
摘自某乎大佬解释:
假设你是一个老师,让30个学生解答一道题目,然后检查学生做的是否正确,你有下面几个选择:
1.第一种选择: (阻塞IO)
按顺序逐个检查,先检查A,然后是B,之后是C D...这中间如果有一个学生卡主,全班都会被耽误.
这种模式就好比,你用循环挨个处理socket,根本不具有并发能力.
2.第二种选择: (非阻塞IO)
你创建30个分身,每个分身检查一个学生的答案是否正确.
这种类似于为每一个用户创建一个进程或者线程处理连接.
3.第三种选择: (IO多路复用)
你站在讲台上等,谁解答完谁举手.
这时C、D举手,表示他们解答问题完毕,你下去依次检查C、D的答案,然后继续回到讲台上等.
此时E、A又举手,然后去处理E和A...
4.第四种选择: (异步IO,自己编的,asyncio
是Python3.4版本引入的标准库,直接内置了对异步IO的支持)
异步IO的思想就是,你给学生命令后,不用特意去管他们做的怎么样了,而是在讲台上织毛衣或嗑瓜子等等,直到学生做完再去检查,然后继续干自己的.
代码略.
捡了基础一个月,拾遗就到这里吧,End!