本节内容:
- 线程
- 进程
- 协程
- IO多路复用
- 自定义异步非阻塞的框架
线程和进程的介绍:
举个例子,拿甄嬛传举列线程和进程的关系:
总结:
1.工作最小单元是线程,进程说白了就是提供资源的
2.一个应用程序至少有一个进程,一个进程里至少有一个线程
3.应用场景:io密集型适合用多线程,计算密集型(cpu)适合用多进程
4.GIL:全局解释器锁,作用:保证同一个进程中只能有一个线程同时被调用
5.python的一个诟病:前提是被cpu调度,因为有GIL,一个应用只有一个进程,纵容有多个线程,也体现不出多核的优势,除非有多个进程,好处是降低的python开发者的难度。
1.线程
Threading用于提供线程相关的操作,线程是应用程序中工作的最小单元,开线程的主要的作用是可以帮我们同时做某些事情。
线程的基本使用:
1.线程的基本使用
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# -*- coding: UTF-8 -*- #blog:http://www.cnblogs.com/linux-chenyang/ import threading import time def task(arg): #隔1s后执行这三十个线程,是并发的,同时工作 time.sleep(1) print('aaaaaaa',arg) for i in range(30): #这个是创建出30个线程来 t = threading.Thread(target=task,args=[i,]) # t.setDaemon(True) #主程序结束,不等待子线程 # t.setDaemon(False) #默认 t.start() # t.join() #等一个线程执行完在执行下一个线程 # t.join(1) #等待最大时间,最多等1秒 print('end') #这个就相当于主线程
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#end是主线程,等主线程创建好后会隔1s并发执行30个函数 end aaaaaaa 0 aaaaaaa 1 aaaaaaa 2 aaaaaaa 4 aaaaaaa 5 aaaaaaa 3 aaaaaaa 6 aaaaaaa 7 aaaaaaa 9 aaaaaaa 8 aaaaaaa 11 aaaaaaa 10 aaaaaaa 13 aaaaaaa 12 aaaaaaa 14 aaaaaaa 17 aaaaaaa 15 aaaaaaa 16 aaaaaaa 19 aaaaaaa 18 aaaaaaa 20 aaaaaaa 21 aaaaaaa 22 aaaaaaa 23 aaaaaaa 25 aaaaaaa 26 aaaaaaa 24 aaaaaaa 27 aaaaaaa 28 aaaaaaa 29
2.线程任务被调用过程
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#线程执行的过程,自定义线程,我们的函数是被run方法调用的 import threading import time class MyThread(threading.Thread): def __init__(self,func,*args,**kwargs): super(MyThread,self).__init__(*args,**kwargs) self.func = func def run(self): self.func() print('-------首先执行的是run方法-----') def task(): print('task函数。。。。') obj=MyThread(func=task) obj.start()
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
task函数。。。。
-------首先执行的是run方法-----
线程锁:
3.线程锁,只能有一个人使用锁
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#用处:这样的话变量v就是来一个减一个,不会出现假如其中有线程没有执行完,在去减就会出错 import threading import time #创建锁 lock = threading.Lock() #lock = threading.RLock() #使用这种方法可以锁多次,但是解锁也要解锁多次,带递归的锁 v=10 def task(arg): time.sleep(2) #申请使用锁,其他人等 lock.acquire() #lock.acquire() global v v -= 1 print(v) #释放 lock.release() #lock.release() for i in range(10): t = threading.Thread(target=task,args=(i,)) #元组的方式最好加逗号,(后续)怕误以为是个函数 t.start()
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
9
8
7
6
5
4
3
2
1
0
4.线程锁,同时多个人使用锁
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import threading import time #创建锁 lock = threading.BoundedSemaphore(3) #同时锁三个线程 v=10 def task(arg): lock.acquire() time.sleep(1) #当进来锁后等1秒,这时显示打印会三条三条的显示。 global v v -= 1 print(v) lock.release() for i in range(10): t = threading.Thread(target=task,args=(i,)) #元组的方式最好加逗号,(后续)怕误以为是个函数 t.start()
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#三个三个显示,等1秒 9 8 7 6 5 4 3 2 1 0
5.线程锁,事件锁的应用
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#3.事件锁,当触发某个条件,我生成的子线程才工作 import threading import time #创建锁 lock = threading.Event() def task(arg): time.sleep(1) #锁住所有的线程 lock.wait() print(arg) for i in range(10): t = threading.Thread(target=task,args=(i,)) t.start() while True: value = input('>>') if value == '1': lock.set() # lock.clear() 默认
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
>>1
>>0
2
4
6
8
1
3
5
7
9
6.线程锁,条件condition用法
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#4.条件,Condition的用法,输入几就释放几个线程 import threading import time #创建锁 lock = threading.Condition() def task(arg): time.sleep(1) #锁住所有的线程 lock.acquire() lock.wait() print('线程',arg) lock.release() for i in range(10): t = threading.Thread(target=task,args=(i,)) t.start() while True: value = input('>>') lock.acquire() lock.notify(int(value)) lock.release()
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
C:\Python35\python3.exe D:/pycharm/s16/day9/2.锁.py >>2 >>线程 0 线程 2 3 >>线程 1 线程 4 线程 3
线程池(先创建连接,在去执行):1.最基本的使用,线程池,若有100个任务,我的线程池总共有5个线程,每个线程处理完任务会继续处理下一个任务。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# -*- coding: UTF-8 -*- #blog:http://www.cnblogs.com/linux-chenyang/ from concurrent.futures import ThreadPoolExecutor import time def task(arg): time.sleep(1) print('线程池',arg) pool = ThreadPoolExecutor(5) for i in range(100): #去连接池中取连接 pool.submit(task,i)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#任务会5个5个来,每隔1秒 C:\Python35\python3.exe D:/pycharm/s16/day9/3.线程连接池.py 线程池 0 线程池 2 线程池 1 线程池 3 线程池 4 线程池 5 线程池 6 线程池 7 线程池 8 线程池 9 线程池 10 线程池 11 线程池 13 线程池 12 线程池 14 线程池 15 线程池 18 线程池 16 线程池 17 线程池 19 线程池 20 线程池 21 线程池 22 线程池 23 线程池 24 线程池 25 线程池 26 线程池 27 线程池 28 线程池 29 线程池 30 线程池 32 线程池 34 线程池 31 线程池 33 线程池 35 线程池 36 线程池 37 线程池 38 线程池 39 线程池 40 线程池 41 线程池 42 线程池 44 线程池 43 线程池 45 线程池 46 线程池 47 线程池 48 线程池 49 线程池 50 线程池 51 线程池 52 线程池 53 线程池 54 线程池 55 线程池 56 线程池 57 线程池 58 线程池 59 线程池 60 线程池 61 线程池 62 线程池 63 线程池 64 线程池 65 线程池 66 线程池 67 线程池 68 线程池 69 线程池 70 线程池 71 线程池 72 线程池 74 线程池 73 线程池 75 线程池 76 线程池 77 线程池 79 线程池 78 线程池 80 线程池 81 线程池 82 线程池 84 线程池 83 线程池 85 线程池 87 线程池 86 线程池 88 线程池 89 线程池 91 线程池 90 线程池 92 线程池 93 线程池 94 线程池 95 线程池 96 线程池 97 线程池 98 线程池 99
2.并发发送http请求,获取结果
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor import time # pip3 install requests import requests # ########## 1. 并发发送Http请求,获取结果 ########## def task(url): response = requests.get(url) print('得到结果:',url,len(response.content)) # .... pool = ThreadPoolExecutor(2) url_list = [ 'http://www.oldboyedu.com', 'http://www.autohome.com.cn', 'http://www.baidu.com', ] for url in url_list: print('开始请求',url) # 去连接池中获取链接 pool.submit(task,url)
3.连接池可以并发去登陆多台主机,去执行命令。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
from concurrent.futures import ThreadPoolExecutor import time # pip3 install requests import requests def task(host): import paramiko ssh = paramiko.SSHClient() ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) ssh.connect(hostname=host, port=22, username='wupeiqi', password='123') stdin, stdout, stderr = ssh.exec_command('df') result = stdout.read() ssh.close() print(result) pool = ThreadPoolExecutor(2) host_list = [ 'c1.com', 'c2.com', 'c3.com', ] for host in host_list: print('开始请求',host) # 去连接池中获取链接 pool.submit(task,host)
创建线程池的思维:若要自定义线程池,线程池里面的线程默认是没有启动线程的,来一个任务起一个线程,来第二个任务先看下第一个线程空闲不空闲,要是不空闲在起第二个线程,以此类推,直到达到最大线程数的值。
4.回调函数
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#线程池之分布式执行,回调执行 #假如我拿到了各个网站的内容,有的人把内容存到本地,有的人把内容上次到网盘 #我们利用线程池只把数据的结果拿到,至于用户怎么操作,那就让他自己写个方法(函数) from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor import requests #我自己的方法 def text(future): download_response = future.result() #6.若要拿到response的内容,就执行future.result()语法 print('我自己的保存数据的方法。。。。',download_response.url,download_response.status_code) def download(url): #4.下载网页的方法 response = requests.get(url) # print('得到结果:',url,len(response.content)) return response #response包含了下载的所有内容 pool = ThreadPoolExecutor(2) #1.先有个线程池 #2.有个各个网站的列表 url_list = [ 'http://www.oldboyedu.com', 'http://www.autohome.com.cn', 'http://www.baidu.com', ] for url in url_list: print('开始请求',url) # 去连接池中获取链接 future=pool.submit(download,url) #3.用线程下载网站的内容 #这就是回调函数:执行完后在调用一个函数 future.add_done_callback(text) #5.获取到网站内容后执行text的方法
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
开始请求 http://www.oldboyedu.com 开始请求 http://www.autohome.com.cn 开始请求 http://www.baidu.com 我自己的保存数据的方法。。。。 http://www.oldboyedu.com/ 200 我自己的保存数据的方法。。。。 http://www.baidu.com/ 200 我自己的保存数据的方法。。。。 http://www.autohome.com.cn/beijing/ 200
5.回调函数优化,把线程和回调函数写成一个公共的模块,用户传人网站内容和方法
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# -*- coding: UTF-8 -*- #blog:http://www.cnblogs.com/linux-chenyang/ from concurrent.futures import ThreadPoolExecutor import requests def download(url): response = requests.get(url) return response #response包含了下载网站的所有内容 # url_list = [ # {'url':'http://www.oldboyedu.com','call':'f1'}, # {'url':'http://www.autohome.com.cn','call':'f1'}, # {'url':'http://www.baidu.com','call':'f1'}, # ] def run(url_list): pool = ThreadPoolExecutor(2) for item in url_list: url = item['url'] call = item['call'] future=pool.submit(download,url) future.add_done_callback(call)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# -*- coding: UTF-8 -*- #blog:http://www.cnblogs.com/linux-chenyang/ import nb_thread def f1(future): response = future.result() print(response.text) #网站的内容以文本的格式显示 def f2(future): response = future.result() def f3(future): response = future.result() url_list = [ {'url':'http://www.oldboyedu.com','call':f1}, {'url':'http://www.autohome.com.cn','call':f2}, {'url':'http://www.baidu.com','call':f3}, ] nb_thread.run(url_list)
2.进程
线程和线程之间资源是共享的,进程和进程之间的资源是不共享的。
进程的基本使用:
1.进程和线程的套路插不多
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# -*- coding: UTF-8 -*- #blog:http://www.cnblogs.com/linux-chenyang/ from multiprocessing import Process import time def task(arg): time.sleep(2) print(arg) if __name__ == '__main__': #windows上必须这么写,linux和mac上不用 for i in range(10): p = Process(target=task,args=(i,)) # p.daemon=True #主进程不等子进程执行完 p.start() # p.join(1) #主进程最多等1秒 print('主进程中的主线程执行到最后。。。。。')
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
主进程中的主线程执行到最后。。。。。 1 2 0 3 5 7 4 6 8 9
2.进程的数据是不共享的,验证如下,和线程做对比
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#2.验证进程数据不共享 #查看进程输出,每次只有一个数据,不会记录上次的数据 from multiprocessing import Process def task(num,li): li.append(num) print(li) if __name__ == '__main__': v=[] for i in range(10): p = Process(target=task,args=(i,v)) p.start() #利用线程,会记录上次的数据 # import threading # # def task(num,li): # li.append(num) # print(li) # # if __name__ == '__main__': # v=[] # for i in range(10): # p = threading.Thread(target=task,args=(i,v)) # p.start()
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
C:\Python35\python3.exe D:/pycharm/s16/day9/进程的基本使用.py [1] [4] [0] [3] [8] [9] [2] [7] [6] [5]
3.进程之间实现共享数据的两种方法(备注:第二种方法不加join会报错,请忽略,原因为没有关闭连接,所以加join不会报错)
特殊的东西
- Array(‘类型’,长度)
- Manager().list() / Manager().dict()
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# -*- coding: UTF-8 -*- #blog:http://www.cnblogs.com/linux-chenyang/ #实现进程之间的共享 # #方法一:基于c语言 # from multiprocessing import Process,Array # # def task(num,li): # li[num]=1 # print(list(li)) # # if __name__ == '__main__': # v = Array('i',10) #i是数据类型(数字),10表示长度,在c语言中列表必须制定数据类型和长度大小,不像python # # print(v) [0,0,0,0,0,0,0,0,0] # for i in range(10): # p = Process(target=task,args=(i,v,)) # p.start() #输出: # [1, 0, 0, 0, 0, 0, 0, 0, 0, 0] # [1, 0, 1, 0, 0, 0, 0, 0, 0, 0] # [1, 0, 1, 0, 0, 1, 0, 0, 0, 0] # [1, 1, 1, 0, 0, 1, 0, 0, 0, 0] # [1, 1, 1, 0, 0, 1, 0, 0, 0, 1] # [1, 1, 1, 0, 0, 1, 0, 0, 1, 1] # [1, 1, 1, 0, 0, 1, 1, 0, 1, 1] # [1, 1, 1, 1, 0, 1, 1, 0, 1, 1] # [1, 1, 1, 1, 0, 1, 1, 1, 1, 1] # [1, 1, 1, 1, 1, 1, 1, 1, 1, 1] #方法二:基于socket实现的 from multiprocessing import Process,Manager def task(num,li): li.append(num) print(li) if __name__ == '__main__': v = Manager().list() #表示特殊的列表,可以共享 for i in range(10): p = Process(target=task,args=(i,v,)) p.start() p.join(1)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
C:\Python35\python3.exe D:/pycharm/s16/day9/5.进程之间的数据共享.py [0] [0, 1] [0, 1, 2] [0, 1, 2, 3] [0, 1, 2, 3, 4] [0, 1, 2, 3, 4, 5] [0, 1, 2, 3, 4, 5, 6] [0, 1, 2, 3, 4, 5, 6, 7] [0, 1, 2, 3, 4, 5, 6, 7, 8] [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
4.进程池,和线程池一样
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# -*- coding: UTF-8 -*- #blog:http://www.cnblogs.com/linux-chenyang/ from concurrent.futures import ProcessPoolExecutor def call(arg): data = arg.result() print(data) def task(arg): print(arg) return arg+100 if __name__ == '__main__': pool = ProcessPoolExecutor(5) for i in range(10): obj=pool.submit(task,i) obj.add_done_callback(call)
3.协程
线程和进程的操作是由程序触发系统接口,最后的执行者是系统;协程的操作则是程序员。
协程存在的意义:对于多线程应用,CPU通过切片的方式来切换线程间的执行,线程切换时需要耗时(保存状态,下次继续)。协程,则只使用一个线程,在一个线程中规定某个代码块执行顺序。
协程的适用场景:当程序中存在大量不需要CPU的操作时(IO),适用于协程;
协程永远是一个线程在执行,对线程的一个分片处理,目的是充分利用线程的资源,单独拿出来用没啥作用,只有遇到IO才会有用greenlet:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# -*- coding: UTF-8 -*- #blog:http://www.cnblogs.com/linux-chenyang/ #greenlet模块需要先下载pip3 install greenlet 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()
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
C:\Python35\python3.exe D:/pycharm/s16/day9/7.协程.py
12
56
34
78
gevent:
根据协程二次开发:IO+线程,实现一个线程分片处理多个任务,充分利用资源
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#根据协程二次开发:IO+线程,实现一个线程分片处理多个任务,充分利用资源 #gevent需要下载 from gevent import monkey; monkey.patch_all() import gevent import requests def f(url): response = requests.get(url) print(response.url,response.status_code) gevent.joinall([ gevent.spawn(f, 'http://www.qq.com/'), gevent.spawn(f, 'http://www.oldboyedu.com/'), gevent.spawn(f, 'http://www.baidu.com/'), ])
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
C:\Python35\python3.exe D:/pycharm/s16/day9/7.协程.py http://www.baidu.com/ 200 http://www.oldboyedu.com/ 200 http://www.qq.com/ 200
4.IO多路复用
http://www.cnblogs.com/wupeiqi/articles/5040823.html
一个线程做到了同时监听多个端口发来的请求,伪造了一种并发的概念,所以的web框架都是基于以下代码演变过来的
1.io多路复用(可以监听多个socket对象)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import socket import select # IO多路复用:8002,8001 # sk1 = socket.socket() sk1.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,1) sk1.bind(('127.0.0.1',8003,)) sk1.listen(5) sk2 = socket.socket() sk2.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,1) sk2.bind(('127.0.0.1',8004,)) sk2.listen(5) inputs = [sk1,sk2,] while True: # IO多路复用,同时监听多个socket对象 # - select,内部进行循环操作(1024) 在系统底层主动查看有没有人给我发来数据,谁发生变化我就拿来用给r,windows上只有select # - poll, 内部进行循环操作 主动查看,和select区别是没有限制,select最大数据1024 # - epoll, 被动告知(边缘触发),通过异步回调实现 r,w,e = select.select(inputs,[],[],0.05) # r = [sk2,] 假如有人访问8002 # r = [sk1,] 假如有人访问8001 # r = [sk1,sk2] 假如同时访问8002和8001 # r = [] #0.05是超时时间,假如没有人访问就是r就是空列表 # r = [conn,] # r = [sk1,Wconn] ####### for obj in r: #r包含所有的对象,sk1,sk2,sk1.conn,sk2.conn if obj in [sk1,sk2]: # 新连接捡来了... print('新连接来了:',obj) conn,addr = obj.accept() #conn这里有连接来了才会往下执行,否则会卡在等待连接 inputs.append(conn) else: # 有连接用户发送消息来了.. print('有用户发送数据了:',obj) data = obj.recv(1024) #建立完连接后客户端发数据了才会往下走,否则会一直等着收数据 obj.sendall(data)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import socket client = socket.socket() client.connect(('127.0.0.1',8003,)) while True: v = input('>>>') client.sendall(bytes(v,encoding='utf-8')) ret = client.recv(1024) print('服务器返回:',ret)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import socket client = socket.socket() client.connect(('127.0.0.1',8004,)) while True: v = input('>>>') client.sendall(bytes(v,encoding='utf-8')) ret = client.recv(1024) print('服务器返回:',ret)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#服务端 C:\Python35\python3.exe D:/pycharm/s16/day9/8.IO多路复用.py 新连接来了: <socket.socket fd=264, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 8003)> 有用户发送数据了: <socket.socket fd=272, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 8003), raddr=('127.0.0.1', 58840)> 新连接来了: <socket.socket fd=268, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 8004)> 有用户发送数据了: <socket.socket fd=276, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 8004), raddr=('127.0.0.1', 58845)> #客户端1 C:\Python35\python3.exe D:/pycharm/s16/day9/9.socket客户端.py >>>1 服务器返回: b'1' >>> #客户端2 C:\Python35\python3.exe D:/pycharm/s16/day9/10.socket客户端.py >>>2 服务器返回: b'2' >>>
2.io多路复用优化
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# -*- coding: UTF-8 -*- #blog:http://www.cnblogs.com/linux-chenyang/ #r里面只接受,w发送 import socket import select # IO多路复用:8002,8001 # sk1 = socket.socket() sk1.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,1) sk1.bind(('127.0.0.1',8003,)) sk1.listen(5) sk2 = socket.socket() sk2.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,1) sk2.bind(('127.0.0.1',8004,)) sk2.listen(5) inputs = [sk1,sk2,] w_inputs = [] while True: # IO多路复用,同时监听多个socket对象 # - select,内部进行循环操作(1024) 在系统底层主动查看有没有人给我发来数据,谁发生变化我就拿来用给r,windows上只有select # - poll, 内部进行循环操作 主动查看,和select区别是没有限制,select最大数据1024 # - epoll, 被动告知(边缘触发),通过异步回调实现 r,w,e = select.select(inputs,w_inputs,inputs,0.05) # r = [sk2,] 假如有人访问8002 # r = [sk1,] 假如有人访问8001 # r = [sk1,sk2] 假如同时访问8002和8001 # r = [] #0.05是超时时间,假如没有人访问就是r就是空列表 # r = [conn,] # r = [sk1,Wconn] ####### for obj in r: #r包含所有的对象,sk1,sk2,sk1.conn,sk2.conn if obj in [sk1,sk2]: # 新连接捡来了... print('新连接来了:',obj) conn,addr = obj.accept() #conn这里有连接来了才会往下执行,否则会卡在等待连接 inputs.append(conn) else: # 有连接用户发送消息来了.. print('有用户发送数据了:',obj) try: data = obj.recv(1024) #建立完连接后客户端发数据了才会往下走,否则会一直等着收数据 except Exception as ex: data='' if data: #假如收到了数据,就在发送 #obj.sendall(data) w_inputs.append(obj) else: obj.close() inputs.remove(obj) w_inputs.remove(obj) for obj in w: obj.sendall(b'ok') w_inputs.remove(obj)
3.模拟sockserver
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import socket import select import threading # IO多路复用:8002,8001 # ############################### 基于select实现服务端的“伪”并发 ############################### """ def process_request(conn): while True: v = conn.recv(1024) conn.sendall(b'1111') sk1 = socket.socket() sk1.bind(('127.0.0.1',8001,)) sk1.listen(5) inputs=[sk1,] while True: # IO多路复用,同时监听多个socket对象 # - select,内部进行循环操作(1024) 主动查看 # - poll, 内部进行循环操作 主动查看 # - epoll, 被动告知 r,w,e = select.select(inputs,[],inputs,0.05) for obj in r: if obj in sk1: # conn客户端的socket conn,addr = obj.accept() t = threading.Thread(target=process_request,args=(conn,)) t.start() """ # import socketserver # # class MyHandler(socketserver.BaseRequestHandler): # def handle(self): # pass # # # server = socketserver.ThreadingTCPServer(('127.0.0.1',8001),MyHandler) # server.serve_forever()
greenlet