IO多路复用

I/O多路复用指:通过一种机制,可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。

Linux中的 select,poll,epoll 都是IO多路复用的机制。

select
 
select最早于1983年出现在4.2BSD中,它通过一个select()系统调用来监视多个文件描述符的数组,当select()返回后,该数组中就绪的文件描述符便会被内核修改标志位,使得进程可以获得这些文件描述符从而进行后续的读写操作。
select目前几乎在所有的平台上支持,其良好跨平台支持也是它的一个优点,事实上从现在看来,这也是它所剩不多的优点之一。
select的一个缺点在于单个进程能够监视的文件描述符的数量存在最大限制,在Linux上一般为1024,不过可以通过修改宏定义甚至重新编译内核的方式提升这一限制。

外,select()所维护的存储大量文件描述符的数据结构,随着文件描述符数量的增大,其复制的开销也线性增长。同时,由于网络响应时间的延迟使得大量
TCP连接处于非活跃状态,但调用select()会对所有socket进行一次线性扫描,所以这也浪费了一定的开销。
 
poll
 
poll在1986年诞生于System V Release 3,它和select在本质上没有多大差别,但是poll没有最大文件描述符数量的限制。
poll和select同样存在一个缺点就是,包含大量文件描述符的数组被整体复制于用户态和内核的地址空间之间,而不论这些文件描述符是否就绪,它的开销随着文件描述符数量的增加而线性增大。

外,select()和poll()将就绪的文件描述符告诉进程后,如果进程没有对其进行IO操作,那么下次调用select()和poll()的时候将
再次报告这些文件描述符,所以它们一般不会丢失就绪的消息,这种方式称为水平触发(Level Triggered)。
 
epoll
 
直到Linux2.6才出现了由内核直接支持的实现方法,那就是epoll,它几乎具备了之前所说的一切优点,被公认为Linux2.6下性能最好的多路I/O就绪通知方法。
epoll可以同时支持水平触发和边缘触发(Edge Triggered,只告诉进程哪些文件描述符刚刚变为就绪状态,它只说一遍,如果我们没有采取行动,那么它将不会再次告知,这种方式称为边缘触发),理论上边缘触发的性能要更高一些,但是代码实现相当复杂。
epoll
同样只告知那些就绪的文件描述符,而且当我们调用epoll_wait()获得就绪文件描述符时,返回的不是实际的描述符,而是一个代表就绪描述符数量的
值,你只需要去epoll指定的一个数组中依次取得相应数量的文件描述符即可,这里也使用了内存映射(mmap)技术,这样便彻底省掉了这些文件描述符在
系统调用时复制的开销。
另一个本质的改进在于epoll采用基于事件的就绪通知方式。在select/poll
中,进程只有在调用一定的方法后,内核才对所有监视的文件描述符进行扫描,而epoll事先通过epoll_ctl()来注册一个文件描述符,一旦基于某
个文件描述符就绪时,内核会采用类似callback的回调机制,迅速激活这个文件描述符,当进程调用epoll_wait()时便得到通知。


Python中有一个select模块,其中提供了:select、poll、epoll三个方法,分别调用系统的 select,poll,epoll 从而实现IO多路复用。

1
2
3
4
5
6
Windows Python:
    提供: select
Mac Python:
    提供: select
Linux Python:
    提供: select、poll、epoll
注意:网络操作、文件操作、终端操作等均属于IO操作,对于windows只支持Socket操作,其他系统支持其他IO操作,但是无法检测 普通文件操作 自动上次读取是否已经变化。

select方法:
句柄列表11, 句柄列表22, 句柄列表33 = select.select(句柄序列1, 句柄序列2, 句柄序列3, 超时时间)
 
参数: 可接受四个参数(前三个必须)
返回值:三个列表
 
select方法用来监视文件句柄,如果句柄发生变化,则获取该句柄。
1、当 参数1 序列中的句柄发生可读时(accetp和read),则获取发生变化的句柄并添加到 返回值1 序列中
2、当 参数2 序列中含有句柄时,则将该序列中所有的句柄添加到 返回值2 序列中
3、当 参数3 序列中的句柄发生错误时,则将该发生错误的句柄添加到 返回值3 序列中
4、当 超时时间 未设置,则select会一直阻塞,直到监听的句柄发生变化
   当 超时时间 = 1时,那么如果监听的句柄均无任何变化,则select会阻塞 1 秒,之后返回三个空列表,如果监听的句柄有变化,则直接执行。

例子:
服务端:
  1. #!/usr/bin/env python
    # Version = 3.5.2
    # __auth__ = '无名小妖'
    import socket, select

    sk = socket.socket()
    sk.bind(('127.0.0.1',9992))
    sk.listen(5)

    inputs = [sk, ]
    while True:
    # 监听sk对象,如果sk发生变化,表示有客户端来连接了,此时rlist的值为sk
    rlist,w,e = select.select(inputs,[],[],1)
    # 监听conn对象,如果conn变化,表示客户端来发消息了,此时的rlist值为 客户端
    print(len(inputs), len(rlist))
    for r in rlist:
    if r == sk: # 新连接
    conn, addr = r.accept()
    inputs.append(conn)
    conn.sendall(bytes('hello !', encoding='utf8'))
    else: # 有人发消息
    r.recv(1024)
客户端:
  1. #!/usr/bin/env python
    # Version = 3.5.2
    # __auth__ = '无名小妖'
    import socket

    client = socket.socket()
    client.connect(('127.0.0.1',9992))

    data = client.recv(1024)
    print(data.decode())
    while True:
    inp = input('>>>')
    client.sendall(bytes(inp, encoding='utf8'))
    print(client.recv(1024).decode())
    client.close()
 

完整例子:
服务端:
  1. #!/usr/bin/env python
    # Version = 3.5.2
    # __auth__ = '无名小妖'
    import socket, select

    sk = socket.socket()
    sk.bind(('127.0.0.1',9992))
    sk.listen(5)

    inputs = [sk, ]
    outputs = []
    while True:
    # 监听sk对象,如果sk发生变化,表示有客户端来连接了,此时rlist的值为sk
    rlist,wlist,e = select.select(inputs, outputs, [], 1)
    # 监听conn对象,如果conn变化,表示客户端来发消息了,此时的rlist值为 客户端
    print(len(inputs), len(rlist), len(outputs), len(wlist))
    for r in rlist:
    if r == sk: # 新连接
    conn, addr = r.accept()
    inputs.append(conn)
    conn.sendall(bytes('hello !', encoding='utf8'))
    else: # 有人发消息
    print('=============')
    try:
    ret = r.recv(1024)
    if not ret:
    raise Exception('断开连接!')
    else:
    outputs.append(r)
    except Exception as e:
    inputs.remove(r)
    for w in wlist:
    w.sendall(bytes('respons ',encoding='utf8'))
    outputs.remove(w)
  1. 客户端:
  1. #!/usr/bin/env python
    # Version = 3.5.2
    # __auth__ = '无名小妖'
    import socket

    client = socket.socket()
    client.connect(('127.0.0.1',9992))

    data = client.recv(1024)
    print(data.decode())
    while True:
    inp = input('>>>')
    client.sendall(bytes(inp, encoding='utf8'))
    print(client.recv(1024).decode())
    client.close()
  2. -----------------------------------------------------------------------------------------
  3. 多线程 多进程

  4. 一个应用程序,可以有多个进程、多个线程,默认是单进程单线程。
  5. python中由于有GIL(全局解释器锁)的存在,所以每次只能使用某个进程中的一个线程。
  6. 但是上述规则只限于使用cpu的时候,如果不实用cpu,那么可以使用多线程。
  7. 因此,在python中想提高并发有如下做法:
  8.   io密集型:可以使用多线程(因为io操作不实用cpu
  9.   计算密集型:使用多进程

  10. 列子:
  11. import time
    def f1(arg, t=None):
    if t:
    t._delete()
    time.sleep(5)
    print(arg)

    # for i in range(10):
    # f1(i)
    # 单进程、单线程的应用程序
    import threading
    t1 = threading.Thread(target=f1, args=(1,))
    # t1.setDaemon(True) # true,表示主线程不等此子线程
    t1.start()# 不代表当前线程会被立即执行
    #t.join(2) # 表示主线程到此,等待 ... 直到子线程执行完毕
    # 参数,表示主线程在此最多等待n秒

    t2 = threading.Thread(target=f1, args=(2,t1))
    t2.start()# 不代表当前线程会被立即执行
    print('end')
    print('end')
    print('end')
    print('end')
    print('end')
  12. 创建线程的两种方式:

  13. 第一种:(常用的)
  14. import threading
    def f1(arg):
    print(arg)

    t = threading.Thread(target=f1, args=(123,))
    t.start()
    t.run()
    run

  15. 第二种:
  16. import threading
    class MyThread(threading.Thread):
    def __init__(self, func,args):
    self.func = func
    self.args = args
    super(MyThread, self).__init__()

    def run(self):
    self.func(self.args)

    def f2(arg):
    print(arg)

    obj = MyThread(f2,123)
    obj.start()

  17. ----------------------------------------------------------------------------------
  18. 队列

  19. python队列是创建在内存的,当程序退出,队列同时清空。
  20. import queue
    # queue.Queue,先进先出队列
    # queue.LifoQueue,后进先出队列
    # queue.PriorityQueue,优先级队列
    # queue.deque,双向对队
  21. 先进先出队列

  22. #!/usr/bin/env python
    # Version = 3.5.2
    # __auth__ = '无名小妖'
    import queue

    q = queue.Queue(2) # 队列最大长度
    q.put(11) # put 放数据
    q.put(22)
    print(q.qsize()) # 查看队列当前元素个数
    q.put(33, timeout=2) # timeout 等待时间,2秒
    q.put(33, block=False) # block 是否阻塞,False表示不阻塞

    print(q.get()) # get 取数据,默认阻塞
    print(q.empty()) # 检查队列是否为空,返回True,False
    # maxsize 最大支持的个数

    # join,task_done,阻塞进程,当队列中任务执行完毕之后,不再阻塞
    import queue
    q = queue.Queue()
    q.put(123)
    q.put(123)
    q.get()
    q.task_done() # 告诉队列取完了
    q.get()
    q.task_done()
    q.join() # 结束
  23. ----------------------------------------------------------------------------------
  24. 生产者消费者模型

  25. 产生原因:提高应对并发的能力

  26. 线程

  27. Threading用于提供线程相关的操作,线程是应用程序中工作的最小单元。
  28. 小例子:
  29. import threading
  30. import time
  31.   
  32. def show(arg):
  33.     time.sleep(1)
  34.     print('thread'+str(arg))

  35. for i in range(10):
  36.     t = threading.Thread(target=show, args=(i,))
  37.     t.start()

  38. print('main thread stop')

  39. 上述代码创建了10个“前台”线程,然后控制器就交给了CPUCPU根据指定算法进行调度,分片执行指令。
  40. 更多方法:
  41. start            线程准备就绪,等待CPU调度
  42. setName      为线程设置名称
  43. getName      获取线程名称
  44. setDaemon   设置为后台线程或前台线程(默认)
  45.                    如果是后台线程,主线程执行过程中,后台线程也在进行,主线程执行完毕后,后台线程不论成功与否,均停止
  46.                     如果是前台线程,主线程执行过程中,前台线程也在进行,主线程执行完毕后,等待前台线程也执行完成后,程序停止
  47. join              逐个执行每个线程,执行完毕后继续往下执行,该方法使得多线程变得无意义
  48. run              线程被cpu调度后自动执行线程对象的run方法
  49. 自定义线程类

  50. import threading
  51. import time

  52. class MyThread(threading.Thread):

  53. def __init__(self,num):

  54. threading.Thread.__init__(self)

  55. self.num = num

  56. def run(self):#定义每个线程要运行的函数

  57. print("running on number:%s" %self.num)

  58. time.sleep(3)

  59. if __name__ == '__main__':

  60. t1 = MyThread(1)

  61. t2 = MyThread(2)

  62. t1.start()

  63. t2.start()


  64. 线程锁:

  65. 由于线程之间是进行随机调度,并且每个线程可能只执行n条,当多个线程同时修改同一条数据时可能会出现脏数据,所以,出现了线程锁 - 同一时刻只允许一个线程操作。
  66. 上面线程的第一个列子就未使用锁。
  67. import threading
  68. import time
  69. NUM = 10

  70. def func(i,l):

  71. global NUM

  72. # 上锁

  73. l.acquire() # 30,5 25m5,20

  74. NUM -= 1

  75. time.sleep(2)

  76. print(NUM,i)

  77. # 开锁

  78. l.release()

  79. # lock = threading.Lock()

  80. # lock = threading.RLock() # 支持多层锁

  81. lock = threading.BoundedSemaphore(5)

  82. for i in range(30):

  83. t = threading.Thread(target=func,args=(i,lock,))

  84. t.start()


  85. 信号量(Semaphore
  86. 互斥锁 同时只允许一个线程更改数据,而Semaphore是同时允许一定数量的线程更改数据 ,比如厕所有3个坑,那最多只允许3个人上厕所,后面的人只能等里面有人出来了才能再进去。
  87. import threading,time
  88. def run(n):
  89. semaphore.acquire()
  90. time.sleep(1)
  91. print("run the thread: %s" %n)
  92. semaphore.release()
  93. if __name__ == '__main__':
  94. num= 0
  95. semaphore  = threading.BoundedSemaphore(5) #最多允许5个线程同时运行
  96. for i in range(20):
  97. t = threading.Thread(target=run,args=(i,))
  98. t.start()
  99. 事件(event

  100. python线程的事件用于主线程控制其他线程的执行,事件主要提供了三个方法 setwaitclear
  101. 事件处理的机制:全局定义了一个“Flag”,如果“Flag”值为 False,那么当程序执行 event.wait 方法时就会阻塞,如果“Flag”值为True,那么event.wait 方法时便不再阻塞。
  102. clear:将“Flag”设置为False
  103. set:将“Flag”设置为True
  104. 列子:
    import threading 
  105. def func(i,e): 
  106.     print(i)
  107. e.wait() # 检测是什么等,如果是红灯,停;绿灯,行 
  108.     print(i+100)
  109. event = threading.Event() 
  110.     for i in range(10): 
  111.         t = threading.Thread(target=func, args=(i,event,)) 
  112.         t.start() 
  113. #======== 
  114. event.clear() # 设置成红灯 
  115. inp = input('>>>') 
  116. if inp == "1": 
  117.     event.set() # 设置成绿灯
  118. 条件(Condition

  119. 使得线程等待,只有满足某条件时,才释放n个线程
  120. 例子一:
  121. import threading
  122. def func(i,con):
  123. print(i)
  124. con.acquire()
  125. con.wait()
  126. print(i+100)
  127. con.release()
  128. c = threading.Condition()
  129. for i in range(10):
  130. t = threading.Thread(target=func, args=(i,c,))
  131. t.start()
  132. while True:
  133. inp = input('>>>')
  134. if inp == 'q':
  135. break
  136. #  以下 为 条件 代码,inp为几 上面线程就会执行几个
  137. c.acquire()
  138. c.notify(int(inp))
  139. c.release()
  140. 例子二:
  141. import threading
  142. # 输入true则执行一个线程
  143. def condition():
  144. ret = False
  145. r = input('>>>')
  146. if r == 'true':
  147. ret = True
  148. else:
  149. ret = False
  150. return ret
  151. def func(i,con):
  152. print(i)
  153. con.acquire()
  154. con.wait_for(condition)
  155. print(i+100)
  156. con.release()
  157. c = threading.Condition()
  158. for i in range(10):
  159. t = threading.Thread(target=func, args=(i,c,))
  160. t.start()
  161. 定时器(Timer

  162. 定时器,指定n秒后执行某操作
  163. from threading import Timer
  164. def hello():
  165. print("hello, world")
  166. t = Timer(1, hello)
  167. t.start()  # after 1 seconds, "hello, world" will be printed
  168. 线程池

  169. 什么是线程池?
  170. 诸如web服务器、数据库服务器、文件服务器和邮件服务器等许多服务器应用都面向处理来自某些远程来源的大量短小的任务。
  171. 构建服务器应用程序的一个过于简单的模型是:每当一个请求到达就创建一个新的服务对象,然后在新的服务对象中为请求服务。
  172. 但当有大量请求并发访问时,服务器不断的创建和销毁对象的开销很大。
  173. 所以提高服务器效率的一个手段就是尽可能减少创建和销毁对象的次数,特别是一些很耗资源的对象创建和销毁,这样就引入了“池”的概念,
  174. “池”的概念使得人们可以定制一定量的资源,然后对这些资源进行复用,而不是频繁的创建和销毁。
  175. 线程池是预先创建线程的一种技术。
  176. 这些线程都是处于睡眠状态,即均为启动,不消耗CPU,而只是占用较小的内存空间。
  177. 当请求到来之后,缓冲池给这次请求分配一个空闲线程,把请求传入此线程中运行,进行处理。
  178. 当预先创建的线程都处于运行状态,即预制线程不够,线程池可以自由创建一定数量的新线程,用于处理更多的请求。
  179. 当系统比较闲的时候,也可以通过移除一部分一直处于停用状态的线程。
  180. 线程池的注意事项
  181. 虽然线程池是构建多线程应用程序的强大机制,但使用它并不是没有风险的。在使用线程池时需注意线程池大小与性能的关系,注意并发风险、死锁、资源不足和线程泄漏等问题。
  182. 1、线程池大小。多线程应用并非线程越多越好,需要根据系统运行的软硬件环境以及应用本身的特点决定线程池的大小。
  183. 一般来说,如果代码结构合理的话,线程数目与CPU 数量相适合即可。
  184. 如果线程运行时可能出现阻塞现象,可相应增加池的大小;如有必要可采用自适应算法来动态调整线程池的大小,以提高CPU 的有效利用率和系统的整体性能。
  185. 2、并发错误。多线程应用要特别注意并发错误,要从逻辑上保证程序的正确性,注意避免死锁现象的发生。
  186. 3、线程泄漏。这是线程池应用中一个严重的问题,当任务执行完毕而线程没能返回池中就会发生线程泄漏现象。
  187. 线程池要点:
  188. 1、通过判断等待的任务数量和线程池中的最大值,取最小值来判断开启多少线程来工作
  189. 比如:
  190. 任务数是3,进程池最大20  ,那么咱们只需要开启3个线程就行了。
  191. 任务数是500,进程池是20,那么咱们只开20个线程就可以了。
  192. 取最小值
  193. 2、实现线程池正在运行,有一个查看的功能,查看一下现在线程里面活跃的线程是多少等待的是多少?
  194. 线程总共是多少,等待中多少,正在运行中多少
  195. 作用:
  196. 方便查看当前线程池状态
  197. 能获取到这个之后就可以当线程一直处于空闲状态
  198. 查看状态用:上下文管理来做,非常nice的一点
  199. 3、关闭线程
  200. 简单线程池的实现:
  201. import queue
  202. import threading
  203. import time
  204. class ThreadPool:
  205. def __init__(self, maxsize=5):
  206. self.maxsize = maxsize
  207. self._q = queue.Queue(maxsize)
  208. for i in range(maxsize):
  209. self._q.put(threading.Thread)
  210. def get_thread(self):
  211. return self._q.get()
  212. def add_thread(self):
  213. self._q.put(threading.Thread)
  214. pool = ThreadPool(5)
  215. def task(arg,p):
  216. print(arg)
  217. time.sleep(1)
  218. p.add_thread()
  219. for i in range(100):
  220. # threading.Thread类
  221. t = pool.get_thread()
  222. obj = t(target=task,args=(i,pool,))
  223. obj.start()

  224. --------------------------------------------------------------------------------------------
  225. Python 进程

  226. 注意:由于进程之间的数据需要各自持有一份,所以创建进程需要非常大的开销。
  227. 进程各自持有一份数据,默认无法共享数据。
  228. 进程使用:

  229. from multiprocessing import Process
  230. from multiprocessing import queues
  231. import multiprocessing
  232. from multiprocessing import Array
  233. def foo(i,arg):
  234. # arg.put(i)
  235. # print('say hi',i,arg.qsize())
  236. arg[i] = i + 100
  237. for item in arg:
  238. print(item)
  239. print('================')
  240. if __name__ == "__main__":
  241. # li = []
  242. # li = queues.Queue(20,ctx=multiprocessing)
  243. li = Array('i', 10)
  244. for i in range(10):
  245. p = Process(target=foo,args=(i,li,))
  246. #p.daemon = True
  247. p.start()
  248. #p.join()
  249. 实现数据共享的方式:
  250. queuesarrayManager.dict
  251. 数据共享:(queues)
  252. from multiprocessing import Process
    from multiprocessing import queues
    import multiprocessing
    def foo(i,arg):
    arg.put(i)
    print('say hi',i,arg.qsize())

    if __name__ == "__main__":
    # li = []
    li = queues.Queue(20,ctx=multiprocessing) # 实现数据共享queues 和 multiprocessing
    for i in range(10):
    p = Process(target=foo,args=(i,li,))
    #p.daemon = True
    p.start()
    #p.join()

  253. 数据共享:(array)
  254. from multiprocessing import Process
    from multiprocessing import Array # 数组,类似列表

    def foo(i,arg):
    arg[i] = i + 100
    for item in arg:
    print(item)
    print('================')
    if __name__ == "__main__":
    li = Array('i', 10)
    for i in range(10):
    p = Process(target=foo,args=(i,li,))
    p.start()
  255. 数据共享:(Manager
  256. from multiprocessing import Process
    from multiprocessing import Manager

    def foo(i,arg):
    arg[i] = i + 100
    print(arg.values())
    if __name__ == "__main__":
    obj = Manager()
    li = obj.dict()
    for i in range(10):
    p = Process(target=foo,args=(i,li,))
    p.start()
    p.join() # 方式二
    # 方式一
    # import time
    # time.sleep(0.1)

  257. 进程锁:

  258. 没锁:
  259. from multiprocessing import Process
    from multiprocessing import Array
    import time

    def foo(i,lis):
    lis[0] = lis[0] - 1
    time.sleep(1)
    print('say hi',lis[0])
    if __name__ == "__main__":
    li = Array('i', 1)
    li[0] = 10
    for i in range(10):
    p = Process(target=foo,args=(i,li))
    p.start()
  260. 有锁:
  261. from multiprocessing import Process
    from multiprocessing import Array
    from multiprocessing import RLock
    import time
    def foo(i,lis,lc):
    lc.acquire()
    lis[0] = lis[0] - 1
    time.sleep(1)
    print('say hi',lis[0])
    lc.release()
    if __name__ == "__main__":
    # li = []
    li = Array('i', 1)
    li[0] = 10
    lock = RLock()
    for i in range(10):
    p = Process(target=foo,args=(i,li,lock))
    p.start()
  262. 进程池:

  263. 进程池内部维护一个进程序列,当使用时,则去进程池中获取一个进程,如果进程池序列中没有可供使用的进进程,那么程序就会等待,直到进程池中有可用进程为止。

  264. 进程池中有两个方法:

  265. apply

  266. apply_async

  267. 例子:
    from multiprocessing import Pool
    import time
    def f1(arg):
    print(arg,'b')
    time.sleep(5)
    print(arg,'a')
    if __name__ == "__main__":
    pool = Pool(5)
    for i in range(30):
    # pool.apply(func=f1,args=(i,))
    pool.apply_async(func=f1,args=(i,))
    # pool.close() # 所有的任务执行完毕
    time.sleep(1)
    pool.terminate() # 立即终止,当前已经执行的任务完毕
    pool.join() # 夯住,前面必须执行close或者terminate方法
    pass
  268. -----------------------------------------------------------------------------------------------------------------

  269. 协程:

  270. 线程和进程的操作是由程序触发系统接口,最后的执行者是系统;协程的操作则是程序员。

  271. 协程存在的意义:对于多线程应用,CPU通过切片的方式来切换线程间的执行,线程切换时需要耗时(保存状态,下次继续)。协程,则只使用一个线程,在一个线程中规定某个代码块执行顺序。

  272. 协程的适用场景:当程序中存在大量不需要CPU的操作时(IO),适用于协程;

  273. 先看一下基本用法:
    greenlet:

  274. from greenlet import greenlet

    def test1():
    print(12)
    gr2.switch()
    print(34)
    gr2.switch()

    def test2():
    print(56)
    gr1.switch()
    print(78)
    gr1 = greenlet(test1)
    gr2 = greenlet(test2)
    gr1.switch()

  275. gevent

  276. import gevent
    def foo():
    print('Running in foo')
    gevent.sleep(0)
    print('Explicit context switch to foo again')
    def bar():
    print('Explicit context to bar')
    gevent.sleep(0)
    print('Implicit context switch back to bar')
    gevent.joinall([
    gevent.spawn(foo),
    gevent.spawn(bar),
    ])

  277. 简单的例子:
  278. from gevent import monkey; monkey.patch_all()
    import gevent
    import requests
    def f(url):
    print('GET: %s' % url)
    resp = requests.get(url)
    data = resp.text
    print('%d bytes received from %s.' % (len(data), url))
    gevent.joinall([
    gevent.spawn(f, 'https://www.python.org/'),
    gevent.spawn(f, 'https://www.yahoo.com/'),
    gevent.spawn(f, 'https://github.com/'),
    ])

  279. 返回结果:
  280. GET: https://www.python.org/
  281. GET: https://www.yahoo.com/
  282. GET: https://github.com/
  283. 47433 bytes received from https://www.python.org/.
  284. 25751 bytes received from https://github.com/.
  285. 462131 bytes received from https://www.yahoo.com/.

  286.  

循序渐进Python3(八) -- 1 -- socket进阶的更多相关文章

  1. Python3实用编程技巧进阶 ☝☝☝

    Python3实用编程技巧进阶  ☝☝☝ 1.1.如何在列表中根据条件筛选数据 # 1.1.如何在列表中根据条件筛选数据 data = [-1, 2, 3, -4, 5] #筛选出data列表中大于等 ...

  2. Python3实用编程技巧进阶✍✍✍

    Python3实用编程技巧进阶  整个课程都看完了,这个课程的分享可以往下看,下面有链接,之前做java开发也做了一些年头,也分享下自己看这个视频的感受,单论单个知识点课程本身没问题,大家看的时候可以 ...

  3. Python3实用编程技巧进阶

    Python3实用编程技巧进阶  整个课程都看完了,这个课程的分享可以往下看,下面有链接,之前做java开发也做了一些年头,也分享下自己看这个视频的感受,单论单个知识点课程本身没问题,大家看的时候可以 ...

  4. python3 进程间通信之socket.socketpair()

    python3 进程间通信之socket.socketpair() socket.socketpair()是什么鬼东西? socket.socketpair()函数仅返回两个已经连接的套接字对象,参数 ...

  5. 循序渐进Python3(八) -- 0 -- 初识socket

    socket通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄,应用程序通常通过"套接字"向网络发出请求或者应答网络请求. socket起源于Un ...

  6. python3.x 基础八:socket网络编程

    Socket socket就是一直以来说的“套接字”,用于描述:ip:端口,是通信链的句柄,客户端通过这个句柄进行请求和响应 普通文件的操作顺序:打开-读写-关闭,针对的是文件 socket是特殊的文 ...

  7. 循序渐进Python3(七) -- 2-- 面向对象进阶

    面向对象是一种编程方式,此编程方式的实现是基于对 类 和 对象 的使用 类 是一个模板,模板中包装了多个“函数”供使用(可以讲多函数中公用的变量封装到对象中) 对象,根据模板创建的实例(即:对象),实 ...

  8. Python3的tcp socket接收不定长数据包接收到的数据不全。

    Python Socket API参考出处:http://blog.csdn.net/xiangpingli/article/details/47706707 使用socket.recv(pack_l ...

  9. Python3.6写socket程序

    Python进行Socket程序编写使用的主要模块就是 socket 模块,在这个模块中可以找到 socket()函数,该函数用于创建套接字对象.套接字也有自己的方法集,这些方法可以实现基于套接字的网 ...

随机推荐

  1. vim常用操作

    vim filename 编辑一个文件 在一般模式里按yy是复制的意思(复制当前行),按yy之前先按相应的数字键就是复制光标所在行到指定的行,然后按p粘贴在一般模式里按dd是删除的意思(也叫做剪切), ...

  2. java 遍历map 方法 集合 五种的方法

    package com.jackey.topic; import java.util.ArrayList;import java.util.HashMap;import java.util.Itera ...

  3. Android中View绘制流程以及invalidate()等相关方法分析

    [原文]http://blog.csdn.net/qinjuning 整个View树的绘图流程是在ViewRoot.java类的performTraversals()函数展开的,该函数做的执行过程可简 ...

  4. 黑马程序员_ Objective-c 面向对象笔记详解

    1)类,对象,方法 类 类名: 1) 类名的第一个字母必须是大写 2) 不能有下划线 3) 多个英文单词,用驼峰标识 类的声明和实现 类的声明 @interface 类名 : NSObject { @ ...

  5. nginx日志分割脚本

    [root@localhost nginx]# cat logs/nginx.pid 5118[root@localhost nginx]# kill -QUIT 5118-QUIT : 关闭进程-H ...

  6. ebs双节点webservice部署问题

    出现异常: oracle.webservices.mdds.MddsException: java.io.IOException: Failed to read WSDL from http://eb ...

  7. JS中的split

    个人理解spilt函数是javascript中字符串和正则表达式的一个联合应用.功能是根据指定的正则表达式(如果是字符串的话也会转为正则表达式)对字符串进行分割.返回值是一个被分割后的字符串数组. 最 ...

  8. android需知小细节

    1. 布局文件命名不能包含大写字母,特殊字符.MyImageView.xml错误. 2. simpleAdapter适配器参数的含义:  3. gridview gridview的三个重要的属性: 4 ...

  9. x-ua-compatible的实践

    前提: 在ie8中测试 页面头部含有<!DOCTYPE html> 结果: <meta http-equiv="x-ua-compatible" content= ...

  10. 怎么解决svn清理失败且路径显示乱码问题

    http://jingyan.baidu.com/article/295430f1d728830c7e0050f9.html 上面这个网址是百度经验给的方法,我也是参照这个方式解决了问题,虽然是解决了 ...