上一章節,我們采用多執行緒的技術去進行服務器埠的掃描,遺留了一些問題待優化,今天,我們采用協程的方式去嘗試一下是否解決這個問題,
協程是一種輕量級的執行緒,協程擁有自己的暫存器背景關系和堆疊,協程調度切換時,將暫存器背景關系和堆疊保存到其他地方,在切回來的時候,恢復先前保存的暫存器背景關系和堆疊,因此:協程能保留上一次呼叫時的狀態,每次程序重入時,就相當于進入上一次呼叫的狀態,也就是說同一執行緒下的一段代碼執行著執行著就可以中斷,然后跳去執行另一段代碼,當再次回來執行代碼塊的時候,接著從之前中斷的地方開始執行,
協程的優點:
1、執行效率高,尤其是在執行緒數較多的情況下,與多執行緒對比的優勢更明顯
2、不需要多執行緒的鎖機制,因為只有一個執行緒,也不存在同時寫變數沖突,在協程中控制共享資源不加鎖,只需要判斷狀態就好,因為執行效率比多執行緒高很多,
缺點:
1、無法利用多核資源:協程的本質是個單執行緒,它不能同時將 單個CPU 的多個核用上,協程需要和行程配合才能運行在多CPU上.當然我們日常所撰寫的絕大部分應用都沒有這個必要,除非是cpu密集型應用,
2、進行阻塞(Blocking)操作(如IO時)會阻塞掉整個程式
接下來,讓我們通過一段代碼來看一下運行的效果:
import gevent
from gevent import monkey
import time
def fun1():
for num in range(3):
print('fun1方法正在運行')
#time.sleep(1)
def fun2():
for num in range(3):
print('fun2方法正在運行')
#time.sleep(1)
# 創建協程物件
t1 = gevent.spawn(fun1)
t2 = gevent.spawn(fun2)
monkey.patch_all()
gevent.joinall([t1, t2])
以上代碼執行的時候,輸出結果如下:
fun1方法正在運行
fun1方法正在運行
fun2方法正在運行
fun2方法正在運行
fun2方法正在運行
是不是跟預想的不一樣呢,是的,因為程式執行沒有阻塞/中斷,所以列印結果沒有交叉列印 ,把time.sleep(1)放開后,再執行:
import gevent
from gevent import monkey
import time
def fun1():
for num in range(3):
print('fun1方法正在運行')
time.sleep(1) # 協程遇到耗時操作后會自動切換其他協程運行
def fun2():
for num in range(3):
print('fun2方法正在運行')
time.sleep(1) # 協程遇到耗時操作后會自動切換其他協程運行
# 創建協程物件
t1 = gevent.spawn(fun1)
t2 = gevent.spawn(fun2)
monkey.patch_all()
gevent.joinall([t1, t2])
列印結果如下:
fun1方法正在運行
fun2方法正在運行
fun1方法正在運行
fun2方法正在運行
fun1方法正在運行
fun2方法正在運行
代碼說明:
本次采用gevent庫實作協程的相關操作,在使用之前需要先安裝該插件,
安裝命令:pip install gevent
gevent.spawn()函式:創建協程物件
gevnet.joinall([傳入攜程物件串列]):會等待所有協程物件運行結束后再退出
接下來改造埠掃描的代碼,采用協程的方式實作:
import socket
import time
import gevent
from gevent import monkey
from gevent.pool import Pool
monkey.patch_all()
def scan_port(host, port):
sk = socket.socket()
sk.settimeout(0.5)
conn_result = sk.connect_ex((host, port))
if conn_result == 0:
print(f'服務器{host}的{port}埠已開放')
sk.close()
def gevent_scan_host(host):
# 8.129.162.225
start_time = time.time()
run_list = []
g = Pool(200) # 限制協程并發數量,單執行緒的,不要設定太大
for port in range(0, 65536):
run_list.append(g.spawn(scan_port, host, port))
gevent.joinall(run_list)
end_time = time.time()
print(f'耗時:{end_time-start_time}')
host = input('請輸入服務器ip地址:')
gevent_scan_host(host)
相關文章都是邊學習邊整理的筆記,如有錯誤,歡迎指正,如需進學習交流群,或者互相交流,文章催更,商務合作等,可添加微信xiaobotester ,記得備注一下,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/290096.html
標籤:python
