併發編程 協程

瀏覽器/爬蟲都是socket 客戶端python

如何提升併發?  多線程  :IO  多進程:計算git

一.IO多路複用程序員

做用:檢測多個socket 是否發生變化(是否鏈接成功/是否獲取數據) (可讀/可寫)github

regests 模塊web

ret=requests.get('https://www.baidu.com/s?wd=alex')
#DNS解析,根據域名解析出IP 後面的爲搜索關鍵字

 

socket面試

 1 #socket 發送請求
 2 import  socket
 3 cilent=socket.socket()
 4 cilent.connect(("www.baidui.com",80))
 5 cilent.sendall(b'GET /s?wd=alex HTTP/1.0\r\nhost:www.baidu.com\r\n\r\n')
 6 data=cilent.recv(8096)
 7 lst=[]
 8 while 1:
 9     if not data:
10         break
11     lst.append(data)
12 list=b"".join(lst)
13 list.decode("utf8")

b"".join(lst)  使列表結成字符串瀏覽器

2.多線程解決併發 多線程

 1 import  socket
 2 import requests
 3 def func(key):
 4     cilent=socket.socket()
 5     cilent.connect(("www.baidui.com",80))
 6     cilent.sendall(b'GET /s?wd=%s HTTP/1.0\r\nhost:www.baidu.com\r\n\r\n' % key)
 7     data=cilent.recv(8096)
 8     lst=[]
 9     while 1:
10         if not data:
11             break
12         lst.append(data)
13     list=b"".join(lst)
14     list.decode("utf8")
15 import  threading
16 l=["zq","wa","wd"]
17 
18 for item  in  l:
19     t1=threading.Thread(target=func,args=(item))
20     t1.start()

缺點: 這個須要等待,由於有阻塞,會在客戶端connect,recv 時刻須要等待客戶端 ,怎樣才能不等待呢???併發

IO多路複用+socket 實現併發請求 (一個線程100個請求)app

這裏用到   client.setblocking(False) 

1 client = socket.socket()
2 client.setblocking(False) # 將原來阻塞的位置變成非阻塞(報錯)
3 # 百度建立鏈接: 阻塞
4 
5 try:#須要 避免
6     client.connect(('www.baidu.com',80)) # 執行了但報錯了
7 except BlockingIOError as e:
8     pass

 

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

IO多路複用+socket 實現併發請求

 1 import socket
 2 import select
 3 
 4 client1 = socket.socket()
 5 client1.setblocking(False) # 百度建立鏈接: 非阻塞
 6 
 7 try:
 8     client1.connect(('www.baidu.com',80))
 9 except BlockingIOError as e:
10     pass
11 
12 client2 = socket.socket()
13 client2.setblocking(False) # 百度建立鏈接: 非阻塞
14 try:
15     client2.connect(('www.sogou.com',80))
16 except BlockingIOError as e:
17     pass
18 
19 client3 = socket.socket()
20 client3.setblocking(False) # 百度建立鏈接: 非阻塞
21 try:
22     client3.connect(('www.oldboyedu.com',80))
23 except BlockingIOError as e:
24     pass
25 
26 socket_list = [client1,client2,client3]#建立鏈接列表,有三個
27 conn_list = [client1,client2,client3]#再建立
28 
29 while True:
30     rlist,wlist,elist = select.select(socket_list,conn_list,[],0.005)#rlist
31     # wlist中表示是否已經和socket創建鏈接對象,有返回值,可寫 返回cilent
32     #rlist中表示是否已經和socket有返回數據,有返回值,可讀 返回cilent
33     #[] 中 將錯誤信息返回空列表
34     #0.005 最大0.005秒檢測錯誤
35     for sk in wlist:
36         if sk == client1:
37             sk.sendall(b'GET /s?wd=alex HTTP/1.0\r\nhost:www.baidu.com\r\n\r\n')
38         elif sk==client2:
39             sk.sendall(b'GET /web?query=fdf HTTP/1.0\r\nhost:www.sogou.com\r\n\r\n')
40         else:
41             sk.sendall(b'GET /s?wd=alex HTTP/1.0\r\nhost:www.oldboyedu.com\r\n\r\n')
42         conn_list.remove(sk)
43     for sk in rlist:
44         chunk_list = []
45         while True:
46             try:
47                 chunk = sk.recv(8096)
48                 if not chunk:#
49                     break
50                 chunk_list.append(chunk)
51             except BlockingIOError as e:
52                 break
53         body = b''.join(chunk_list)
54         # print(body.decode('utf-8'))
55         print('------------>',body)
56         sk.close()
57         socket_list.remove(sk)
58     if not socket_list:#這裏指當列表中的cilent被取到時,把他們移出做爲判斷
59         #直到把最後都取出
60         break

用面向對象作IO多路複用

import socket
import select

class Req(object):
    def __init__(self,sk,func):
        self.sock = sk
        self.func = func

    def fileno(self):
        return self.sock.fileno()


class Nb(object):

    def __init__(self):
        self.conn_list = []
        self.socket_list = []

    def add(self,url,func):
        client = socket.socket()
        client.setblocking(False)  # 非阻塞
        try:
            client.connect((url, 80))
        except BlockingIOError as e:
            pass
        obj = Req(client,func)
        self.conn_list.append(obj)
        self.socket_list.append(obj)

    def run(self):

        while True:
            rlist,wlist,elist = select.select(self.socket_list,self.conn_list,[],0.005)
            # wlist中表示已經鏈接成功的req對象
            for sk in wlist:
                # 發生變換的req對象
                sk.sock.sendall(b'GET /s?wd=alex HTTP/1.0\r\nhost:www.baidu.com\r\n\r\n')
                self.conn_list.remove(sk)
            for sk in rlist:
                chunk_list = []
                while True:
                    try:
                        chunk = sk.sock.recv(8096)
                        if not chunk:
                            break
                        chunk_list.append(chunk)
                    except BlockingIOError as e:
                        break
                body = b''.join(chunk_list)
                # print(body.decode('utf-8'))
                sk.func(body)
                sk.sock.close()
                self.socket_list.remove(sk)
            if not self.socket_list:
                break

def baidu_repsonse(body):
    print('百度下載結果:',body)

def sogou_repsonse(body):
    print('搜狗下載結果:', body)

def oldboyedu_repsonse(body):
    print('老男孩下載結果:', body)

t1 = Nb()
t1.add('www.baidu.com',baidu_repsonse)
t1.add('www.sogou.com',sogou_repsonse)
t1.add('www.oldboyedu.com',oldboyedu_repsonse)
t1.run()
View Code

3.協程

 概念:

進程,線程 操做系統中存在

協程,是由程序員創造出來的,不是一個單獨存在的東西

協程:是爲線程,對一個線程進行分片,使得線程在代碼塊之間來回切換執行,而不是在原來逐行執行

 1 import  greenlet
 2 def func1():
 3     print(11)
 4     f2.switch()
 5     print(22)
 6     f2.switch()
 7 def func2():
 8     print(33)
 9     f1.switch()
10     print(44)
11 f1=greenlet.greenlet(func1)
12 f2=greenlet.greenlet(func2)
13 f1.switch()
協程示例

可是單獨的協程沒有什麼做用

協程只是單純的分片,可是用在線程上就能夠節省寶貴的時間,

可是沒法判斷是不是IO進行分片,這時候就須要  pip3 install gevent 

 1 from gevent import monkey
 2 monkey.patch_all()#之後代碼中遇到IO都會自動執行greenlet的switch進行切換
 3 import requests
 4 import gevent
 5 def f1(url):
 6     ret=requests.get(url)
 7     print(url,ret.content)
 8 def f2(url):
 9     ret=requests.get(url)
10     print(url,ret.content)
11 def f3(url):
12     ret=requests.get(url)
13     print(url,ret.content)
14 gevent.joinall([
15     gevent.spawn(f1,'https://www.python.org/'),#協程1
16     gevent.spawn(f2, 'https://www.yahoo.com/'),
17     gevent.spawn(f3, 'https://www.github.com/'),
18 ])
monkey.patch_all()

 

總結:
1. 什麼是協程?
協程也能夠稱爲「微線程」,就是開發者控制線程執行流程,控制先執行某段代碼而後再切換到另外函執行代碼...來回切換。

2. 協程能夠提升併發嗎?
協程本身自己沒法實現併發(甚至性能會下降)。
協程+IO切換性能提升。

3. 進程、線程、協程的區別?

4. 單線程提供併發:
- 協程+IO切換:gevent
- 基於事件循環的異步非阻塞框架:Twisted
手動實現協程:yield關鍵字生成器 
 1             def f1():
 2                 print(11)
 3                 yield
 4                 print(22)
 5                 yield
 6                 print(33)
 7 
 8             def f2():
 9                 print(55)
10                 yield
11                 print(66)
12                 yield
13                 print(77)
14 
15             v1 = f1()
16             v2 = f2()
17 
18             next(v1) # v1.send(None)
19             next(v2) # v1.send(None)
20             next(v1) # v1.send(None)
21             next(v2) # v1.send(None)
22             next(v1) # v1.send(None)
23             next(v2) # v1.send(None)
View Code

整體總結

 1 '''
 2 1.本質上socket
 3 2.如何提升併發 ?
 4 多進程:計算 多線程:io
 5 一.
 6 1.經過客戶端協議也能夠瀏覽網頁
 7 2.協議 遵循
 8 3.間接能夠用requests 模塊  或者用 socket 模塊來引用
 9 4.cilent.setblocking(False)將原來阻塞的位置變成非阻塞(報錯)
10 報錯捕獲一下  try  except BlockingIOError as e:
11 做用:單線程遇到io 不等待
12 須要檢測是否鏈接
13 ***  io多路複用做用: 檢測socket是否已將發生變化
14 (是否已經鏈接成功/是否已經獲取數據) (可度/可寫)
15 ***
16 5. select.select()[] >>報錯寫入  0.005 >內部最多0.005檢測錯誤
17 6.已經實現的模塊 Twisted  基於事件循環實現的異步非阻塞框架
18 還用封裝實現 (沒聽好)
19 7.總結;
20 1.socket默認阻塞,阻塞體如今
21  connect recv
22 2. 如何讓socket變成非阻塞
23  setblocking(False)
24 3. io多路複用
25  檢測多個socket是否發生變化
26 4. 系統檢測socket是否發生變化,三種模式
27 select "最多1024個socket;循環檢測
28  poil: (主動查詢)不限制監聽sockrt個數;循環檢測(慢)水平觸發
29 epoil : (舉手,本身完成主動通知) 回調方式 邊緣觸發
30 Python模塊:
31 select.select 
32 select.epoll 
33 
34 5.提升併發方案:
35 多進程
36 多線程
37 異步非阻塞模塊(Twisted)  scrapy框架(單線程完成併發)
38 5.提升併發方案:
39 控制好線程個數
40 生產者消費者模型
41 6.什麼是 異步非阻塞?
42 -非阻塞 ,不等待
43 好比建立socket對某個地址進行connect、獲取接收數據recv時默認都會等待(鏈接成功或接收到數據),才執行後續操做。
44 若是設置setblocking(False),以上兩個過程就再也不等待,可是會報BlockingIOError的錯誤,只要捕獲便可。
45 異步:
46 返回一個函數繼續調用,
47 通知,執行完成後自動執行回調函數局或自動執行某種操做
48 7.什麼是同步阻塞?
49 阻塞 ,等
50 同步,按照順序逐步執行
51 
52 不如建立socket對某個地址
53 
54 
55 三.
56 協程,是由程序員創造出來的不是真實存在的東西
57 由用戶控制
58 協程,就是微線程,對一個線程進程分片,使得線程在代碼塊之間來回切換執行,
59 而不是在原來逐行執行
60 單純的協程無用
61 協程的存在乎義:相似於單線程實現併發:遇到IO + 協程的切換能夠提升性能
62 3.1
63 協程+IO切換>>站起來了  pip3 install gevent
64 from gevent import monkey
65 monkey.path_all() #默認將代碼遇到的IO
66 總結:
67 1.什麼是協程?
68 2.協程能夠提升併發麼?
69 3.進程,線程,協程的區別面試題?
70 4.單線程提供併發
71 -協程+IO切換 :gevent
72 -基於事件循環的異步非阻塞框架: Twisted
73 
74 也能夠經過yield
75 
76 之後gevent  Twisted 寫代碼
77 三種模式:
78 select
79 poil
80 epoil
View Code
相關文章
相關標籤/搜索