1. 程式人生 > 實用技巧 >python3--程序,執行緒,協程效率對比

python3--程序,執行緒,協程效率對比

python3--程序,執行緒,協程效率對比

需求:寫一個指令碼,判斷192.168.11.0/24網路裡,當前線上ip有哪些?

知識點:

1 使用subprocess模組,來呼叫系統命令,執行ping 192.168.11.xxx 命令

2 呼叫系統命令執行ping命令的時候,會有返回值(ping的結果),需要用到stdout=fnull, stderr=fnull方法,遮蔽系統執行命令的返回值

常規版本(程式碼)

import os
import time
import subprocess
def ping_call():
    start_time = time.time()
    fnull = open(os.devnull, 'w')
    for i in range(1, 256):
        ipaddr = 'ping 192.168.11.' + str(i)
        result = subprocess.call(ipaddr + ' -n 2', shell=True, stdout=fnull, stderr=fnull)
        current_time = time.strftime('%Y%m%d-%H:%M:%S', time.localtime())
        if result:
            print('時間:{} ip地址:{} ping fall'.format(current_time, ipaddr))
        else:
            print('時間:{} ip地址:{} ping ok'.format(current_time, ipaddr))
    print('程式耗時{:.2f}'.format(time.time() - start_time))
    fnull.close()
ping_call()

執行效果:

上面的執行速度非常慢,怎麼能讓程式執行速度快起來?

python提供了程序,執行緒,協程。分別用這三個對上面程式碼改進,提高執行效率,測試一波效率

程序池非同步執行 -- 開啟20個程序

import os
import time
import subprocess
from multiprocessing import Pool
def ping_call(num):
    fnull = open(os.devnull, 'w')
    ipaddr = 'ping 192.168.11.' + str(num)
    result = subprocess.call(ipaddr + ' -n 2', shell=True, stdout=fnull, stderr=fnull)
    current_time = time.strftime('%Y%m%d-%H:%M:%S', time.localtime())
    if result:
        print('時間:{} ip地址:{} ping fall'.format(current_time, ipaddr))
    else:
        print('時間:{} ip地址:{} ping ok'.format(current_time, ipaddr))

    fnull.close()


if __name__ == '__main__':
    start_time = time.time()
    p = Pool(20)
    res_l = []
    for i in range(1, 256):
        res = p.apply_async(ping_call, args=(i,))
        res_l.append(res)
    for res in res_l:
        res.get()
    print('程式耗時{:.2f}'.format(time.time() - start_time))

執行結果:

執行緒池非同步執行 -- 開啟20個執行緒

import os
import time
import subprocess
from concurrent.futures import ThreadPoolExecutor
def ping_call(num):
    fnull = open(os.devnull, 'w')
    ipaddr = 'ping 192.168.11.' + str(num)
    result = subprocess.call(ipaddr + ' -n 2', shell=True, stdout=fnull, stderr=fnull)
    current_time = time.strftime('%Y%m%d-%H:%M:%S', time.localtime())
    if result:
        print('時間:{} ip地址:{} ping fall'.format(current_time, ipaddr))
    else:
        print('時間:{} ip地址:{} ping ok'.format(current_time, ipaddr))
    fnull.close()

if __name__ == '__main__':
    start_time = time.time()
    thread_pool = ThreadPoolExecutor(20)
    ret_lst = []
    for i in range(1, 256):
        ret = thread_pool.submit(ping_call, i)
        ret_lst.append(ret)
    thread_pool.shutdown()
    for ret in ret_lst:
        ret.result()
    print('執行緒池(20)非同步-->耗時{:.2f}'.format(time.time() - start_time))

執行結果:

協程執行---(執行多個任務,遇到I/O操作就切換)

使用gevent前,需要pip install gevent

from gevent import monkey;monkey.patch_all()
import gevent
import os
import time
import subprocess

def ping_call(num):
    fnull = open(os.devnull, 'w')
    ipaddr = 'ping 192.168.11.' + str(num)
    result = subprocess.call(ipaddr + ' -n 2', shell=True, stdout=fnull, stderr=fnull)
    current_time = time.strftime('%Y%m%d-%H:%M:%S', time.localtime())
    if result:
        print('時間:{} ip地址:{} ping fall'.format(current_time, ipaddr))
    else:
        print('時間:{} ip地址:{} ping ok'.format(current_time, ipaddr))
    fnull.close()

def asynchronous(): # 非同步
    g_l = [gevent.spawn(ping_call, i) for i in range(1, 256)]
    gevent.joinall(g_l)

if __name__ == '__main__':
    start_time = time.time()
    asynchronous()
    print('協程執行-->耗時{:.2f}'.format(time.time() - start_time))

執行結果:

遇到I/O操作,協程的效率比程序,執行緒高很多!

總結:python中,涉及到I/O阻塞的程式中,使用協程的效率最高

最後附帶協程池程式碼

gevent.pool

from gevent import monkey;monkey.patch_all()
import gevent
import os
import time
import subprocess
import gevent.pool

def ping_call(num):
    fnull = open(os.devnull, 'w')
    ipaddr = 'ping 192.168.11.' + str(num)
    result = subprocess.call(ipaddr + ' -n 2', shell=True, stdout=fnull, stderr=fnull)
    current_time = time.strftime('%Y%m%d-%H:%M:%S', time.localtime())
    if result:
        print('時間:{} ip地址:{} ping fall'.format(current_time, ipaddr))
    else:
        print('時間:{} ip地址:{} ping ok'.format(current_time, ipaddr))
    fnull.close()

if __name__ == '__main__':
    start_time = time.time()
    res_l = []
    p = gevent.pool.Pool(100)
    for i in range(1, 256):
        res_l.append(p.spawn(ping_call, i))
    gevent.joinall(res_l)
    print('協程池執行-->耗時{:.2f}'.format(time.time() - start_time))

執行結果:

本文參與騰訊雲自媒體分享計劃,歡迎正在閱讀的你也加入,一起分享。