查漏补缺--python小细节拾遗(四)

进入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结束
利用gevent吃喝玩乐

  最后,总个结:

 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!

 

 

 

 

 

 

 

 

 

 

  

 

 

 

 

 

 

 

 

 

 

你可能感兴趣的:(查漏补缺--python小细节拾遗(四))