Python--進程與線程
一,回顧操作系統的概念
操作系統位於底層硬件與應用軟件之間的一層
工作方式:向下管理軟件,向上提供接口
二,進程線程的概念
進程是一個資源單位,線程是一個最小的執行單位
一個線程只能屬於一個進程,而一個進程可以有多個線程,但至少有一個線程
三,並行與並發
並行:
就是有多個進程可以同時運行的叫做並行
並發:
就是在一個處理器的情況下,切換執行,叫做並發
python無法實現並行處理,因為全局解釋器鎖gil導致同一時刻同一進程
只能有一個線程被運行。
GIL全局解釋器鎖
但是不影響Python開多進程
多線程代碼示例
import threading importtime ‘‘‘ 程序在運行是有一個主線程,當程序開啟多線程的時候,主線程依舊會執行, 主線程執行到最後時,並沒有結束,而是在等待子線程的結束後主線程結束 ‘‘‘ def misc(): print("聽歌") time.sleep(3) print("聽歌結束") def xieboke(): print("寫博客") time.sleep(5) print("寫博客結束") #開啟線程 t1=threading.Thread(target=misc)#t1,t2是一個線程對象 t2=threading.Thread(target=xieboke) t1.start() t2.start()print("主線程")
#開啟多線程的另一種方式
import threading import time class MyThread(threading.Thread): ‘‘‘ 用類的繼承,繼承線程的方法開啟線程 ‘‘‘ def __init__(self,num): ‘‘‘ 繼承父類的__init__方法 ‘‘‘ threading.Thread.__init__(self) self.num=num def run(self): print("running on mythread:%s"%self.num) time.sleep(3) print("end%s"%self.num) t1=MyThread(10) t2=MyThread(20) t1.start() t2.start() print("主線程")
jion的使用
t.jion方法會阻塞主進程的運行,但不會影響其他線程的運行
setDaemon方法
-守護線程
當某個線程設置為守護線程的時候,它會隨著主線程的結束而結束
t.setDaemon(True)
線程對象下的幾個方法:
-isAlive()檢測線程是否活動,返回值是布爾值
-getName():返回線程名
-setName():設置線程名稱
threading模塊提供的一些方法:
threading.currentTread():返回當前線程變量
threading.enumerate():返回一個包含正在運行的線程的list。
threading.activeCount():返回正在運行的線程數量
Python對於計算密集型運行比較慢,效率低;對於IO密集型效率有明顯提高
Python多線程
互斥鎖:
互斥鎖的意義就是在保護鎖內代碼同一時間只有一個線程在使用
直到代碼執行完成,解鎖後其他線程才能執行所內代碼。
使用格式:
-lock=threading.Lock()創建一把鎖的對象
lock.acquire()#加鎖
....需要保護的執行語句
lock.release()#解鎖
死鎖與遞歸鎖
代碼示例:
import threading import time muteA=threading.Lock() muteB=threading.Lock() class MyThread(threading.Thread): def __init__(self): threading.Thread.__init__(self) def run(self): self.func1() self.func2() def func1(self): muteA.acquire() print("鎖A執行內容",MyThread.getName(self)) muteB.acquire() print("鎖B執行內容",MyThread.getName(self)) muteB.release() muteA.release() def func2(self): muteB.acquire() print("第二個函數的鎖B",MyThread.getName(self)) muteA.acquire() print("第二個函數的鎖A",MyThread.getName(self)) muteA.release() muteB.release() if __name__=="__main__": for i in range(10): my_thread=MyThread() my_thread.start()
形成死鎖的原因在於當線程1在第二個函數中拿到鎖B向下執行需要鎖A的時候,線程2在函數1中
已經拿到的鎖A,在等待線程1釋放B。兩個線程都沒有釋放另一個線程需要的鎖,所以就形成了死鎖。
遞歸鎖的應用
遞歸鎖未避免死鎖的產生,在鎖內實行一個引用計數,當有一把使用是計速器加一,釋放後,去除計數
到代碼在執行鎖內代碼時,如果有其他線程搶鎖,計數如果為零,線程可以拿到鎖,大於零,拒絕線程拿鎖
這樣就能避免鎖的重復,也就不會產生死鎖
代碼示例:
import threading
import time
Rlock=threading.Rlock()
class MyThread(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
def run(self):
self.func1()
self.func2()
def func1(self):
Rlock.acquire()
print("鎖A執行內容",MyThread.getName(self))
Rlock.acquire()
print("鎖B執行內容",MyThread.getName(self))
Rlock.release()
Rlock.release()
def func2(self):
Rlock.acquire()
print("第二個函數的鎖B",MyThread.getName(self))
Rlock.acquire()
print("第二個函數的鎖A",MyThread.getName(self))
Rlock.release()
Rlock.release()
if __name__=="__main__":
for i in range(10):
my_thread=MyThread()
my_thread.start()
event方法使用:
event方法可以讓兩個線程之間通信,當一個線程需要另一個線程準備數據的時候,
event.wait(),阻塞程序的運行,直到另一個線程將數據準備完成後,使用event.set()
返回一個true值,event.wait()接受到該值之後,線程開始運行。wait方法後可以接一個超時
時間參數,規定在一定時間內阻塞,超時後運行。
import threading
import time
import logging
logging.basicConfig(level=logging.DEBUG, format=‘(%(threadName)-10s) %(message)s‘,)
def worker(event):
logging.debug(‘Waiting for redis ready...‘)
while not event.isSet():
logging.debug("wait.......")
event.wait(3) # if flag=False阻塞,等待flag=true繼續執行
logging.debug(‘redis ready, and connect to redis server and do some work [%s]‘, time.ctime())
time.sleep(1)
def main():
readis_ready = threading.Event() # flag=False創建一個event對象
t1 = threading.Thread(target=worker, args=(readis_ready,), name=‘t1‘)
t1.start()
t2 = threading.Thread(target=worker, args=(readis_ready,), name=‘t2‘)
t2.start()
logging.debug(‘first of all, check redis server, make sure it is OK, and then trigger the redis ready event‘)
time.sleep(6) # simulate the check progress
readis_ready.set() # flag=Ture
if __name__=="__main__":
main()
進程multprocessing模塊
multprocessing模塊與threading模塊使用同一套api,使用方法調用方法與threading模塊一樣
代碼示例:
from multiprocessing import Process
import time
def f(name):
print("hello",name,time.ctime())
time.sleep(1)
if __name__=="__main__":
p_list=[]
for i in range(3):
p=Process(target=f,args=("alvin:%s"%i,))
p_list.append(p)
p.start()
協程的應用:
協程是單線程的,不能切換。因為協程對IO操作的判斷由自己控制
import time
# 可以實現並發
def consumer():
r = ‘‘
while True:
n = yield r
if not n:
return
print(‘[CONSUMER] ←← Consuming %s...‘ % n)
time.sleep(1)
r = ‘200 OK‘
def produce(c):
next(c)
n = 0
while n < 5:
n = n + 1
print(‘[PRODUCER] →→ Producing %s...‘ % n)
cr = c.send(n)
print(‘[PRODUCER] Consumer return: %s‘ % cr)
c.close()
if __name__==‘__main__‘:
c = consumer()
produce(c)
gevent模塊的使用:
from gevent import monkey
monkey.patch_all()
import gevent
from urllib import request
import time
def f(url):
print(‘GET: %s‘ % url)
resp = request.urlopen(url)
data = resp.read()
print(‘%d bytes received from %s.‘ % (len(data), url))
start=time.time()
gevent.joinall([
gevent.spawn(f, ‘https://itk.org/‘),
gevent.spawn(f, ‘https://www.github.com/‘),
gevent.spawn(f, ‘https://zhihu.com/‘),
])
#f(‘https://itk.org/‘)
#f(‘https://www.github.com/‘)
#f(‘https://zhihu.com/‘)
print(time.time()-start)
Python--進程與線程