python进程线程协程区别_Python3多线程与协程
python中的多線程非常的常用,之前一直糊里糊涂地使用,沒有一些系統性的概念,記錄一下~
0x001 多線程的優勢:可將長時間占用的程序放到后臺
可能會加速程序執行速度
能夠實現一些類似同步執行的效果
0x002 線程線程是OS的執行單元
每個獨立的線程都有一個程序運行的入口、順序執行序列和程序出口,線程不能離開程序獨立執行。
每個線程都有自己唯一的一組CPU寄存器(上下文),反映了線程上次運行時該CPU寄存器的狀態。線程中指令指針和堆棧指針寄存器非常重要,線程在進程中得到上下文,這些地址用于標志擁有線程的進程地址空間中的內存
線程可被搶占
線程退讓
0x01 分類:名字
說明
用戶線程
不需內核支持而在用戶程序中實現的線程
內核線程
操作系統內核創建和撤銷
0x02 Py3中的threading模塊:方法
說明
threading.current_thread()
返回當前的線程變量
threading.enumerate()
返回正在運行線程的list(啟動后,結束前)
threading.active_count
等于len(threading.enumerate())
提供了 Thread類,所以還可以
方法
說明
run()
表示線程活動的方法
start()
啟動線程活動
join([time])
阻塞式等待線程終止
isAlive()
線程是否活動
getName()
返回線程名
setName()
設置線程名
# -*- coding:utf-8 -*-
# 多線程
# DYBOY
# time:2019-3-10 09:37:48
import threading
import time
def printNum(endNum):
for i in range(1,endNum+1):
print(i, time.time())
# 創建線程
t = threading.Thread(target=printNum, name='printThread', args=(10,))
t.start()
t.join()
print("線程%s結束" % threading.current_thread().name)
0x003 多線程&多進程&線程鎖多進程中同一變量,各自有拷貝到自己的進程中,互不影響,多線程中,變量由多個線程共享,因此多線程中變量的同步就需要的到控制
lock = threading.Lock()
def runThread():
for i in range(1000):
lock.acquire()
try:
#....執行函數
finally:
lock.release()
# -*- coding:utf-8 -*-
# 多線程
# DYBOY
# time:2019-3-10 09:37:48
import threading
import time
money = 0
lock = threading.Lock()
def chaneMoney(num):
global money
money += num
money -= num
def runThread(n):
for i in range(1000):
lock.acquire()
try:
chaneMoney(n)
finally:
lock.release()
t1 = threading.Thread(target=runThread, args=(100,))
t2 = threading.Thread(target=runThread, args=(50,))
t1.start()
t2.start()
t1.join()
t2.join()
print("余額:",money)
ps: 在實際的運行中,發現似乎線程鎖沒有起到作用,在線程中的join() 方法似乎是有影響的,
join():阻塞當前進程/線程,直到調用join方法的那個進程執行完,再繼續執行當前進程。相當于線程守護,直到調用join()方法的線程執行完畢,才將控制權交給主進程。
0x04 問題?
從上,看到多線程中為了保證數據的一致性,使用了線程鎖來實現類似同步的功能,然而這樣反而多了獲取鎖和釋放鎖的步驟,所以在我看來。線程也沒有加快程序的運行時間。
一個程序從執行到結束,首先會創建一個主進程,os的執行單元是線程,一個進程有至少一個或多個線程來實現其功能,在線程的創建和上下文切換是一個比較大的開銷,提升多線程的優勢就需要從其中來考慮:
無鎖并發(減少數據關聯度,更合理優化的實現方式)
減少并發(線程不能無限制的多)
減少上下文切換的開銷(協程)
0x05 協程函數調用的時候,是使用棧的方式,比如A調用B,B調用C,C執行返回給B,B執行完后返回給A,是一個壓棧出棧的過程
子程序(函數),總是一個入口,一次返回,調用的順序永遠如此,所以如果有比較頻繁的函數調用,那么就用較多的上下文切換時間,利用協程(微線程)可以較好解決這個問題。
協程是一個線程執行,那怎么利用多核CPU呢?最簡單的方法是多進程+協程,既充分利用多核,又充分發揮協程的高效率,可獲得極高的性能。(多進程)
# -*- coding:utf-8 -*-
# 協程 gevent
# DYBOY
# time:2019-3-10 09:37:48
# description: 下載圖片到本地(普通版本)
# from gevent import monkey
# monkey.patch_all()
import requests,time,json
def get_save_pic(picUrl, name):
img = requests.get(picUrl)
with open('pic/'+name,'wb') as f:
f.write(img.content)
return None
if __name__ == '__main__':
sT = time.time()
jsonData = requests.get('http://img.top15.cn/piclist.php')
jsonData = json.loads(jsonData.text)
imgs = jsonData['data']
for img in imgs:
get_save_pic(img[0], img[1])
print("Success", time.time() - sT)
網絡效果好的時候:
# -*- coding:utf-8 -*-
# 協程 gevent
# DYBOY
# time:2019-3-10 09:37:48
# description: 下載圖片到本地
from gevent import monkey
monkey.patch_all()
import gevent,requests,time,json
def get_save_pic(picUrl, name):
img = requests.get(picUrl)
with open('pic/'+name,'wb') as f:
f.write(img.content)
return None
if __name__ == '__main__':
startTime = time.time()
jsonData = requests.get('http://img.top15.cn/piclist.php')
jsonData = json.loads(jsonData.text)
imgs = jsonData['data']
targetLists = []
for img in imgs:
targetLists.append(gevent.spawn(get_save_pic, img[0], img[1]))
gevent.joinall(targetLists)
print("Success!", time.time()-startTime)
# 不知道什么原因,沒有輸出,但是從執行的結果上來看
# 最后,所有圖片同時在文件夾生成,非常迅速
2019-3-10 22:05:04 在命令行下可正常執行!
從肉眼可見的角度來看,還是協程的效果更好(在數據量不大下,感覺比較而得出的結論還是不是很有說服力,在數據量大的情況下,線程不能無限增加,協程的效果表現更優異,再加上多進程應該就更NICE了)。
0x06 總結
本次探究的是多線程與協程的區別,多線程不能無限創建,所以有的時候創建多線程在生產環境下是不可行的,在爬蟲下載圖片這部分是可以使用多線程去下載,多線程其實也是一個等待執行的過程,其與協程的差別主要是在上下文切換上,協程減少了上下文切換的時間,是程序自己控制的,而多線程的上下文切換是需要系統調用會耗費更多的時間,本次例子實現中使用了monkey這個模塊,還不清楚其中遇到的輸出問題,繼續探究!
總結
以上是生活随笔為你收集整理的python进程线程协程区别_Python3多线程与协程的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: mysql udf提权_MySQL日志安
- 下一篇: python定义私有变量的方法_Pyth