python多线程 不在main_Python多线程
一、線程概念
在程序運行時,操作系統會創建一個進程,并且會創建一個線程,這個線程就是主線程,主線程可以創建子線程。線程看上去同時運行,其實是按照并發執行的,走走停停,一直到所有形線程完成為止。線程像進程一樣會有生命周期,如下所示:
將程序進行多線程編程,其性能會得到很大提升。python線程對CPU密集型性能提高不大,對I/O密集型性能提高很大。
二、多線程示例
import threading
import time
class myThread(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
def run(self):
out() #加入執行的程序代碼
def out():
print_time1()
print_time2()
def print_time1():
print("Locka is acquired")
print("Lockb is acwuired")
def print_time2():
print("Lockb is acquired")
time.sleep(2)
print("Locka is acwuired")
def main():
for i in range(50):
t = myThread()
t.start() #執行線程
main()
以上就是線程的簡單程序,我們創建了50個線程,將他們同時運行。他們完成的時間不一樣,其先后順序也不能確定。使用方法就是自己寫一個類,繼承threading.Thread類,并重寫方法run(),將自己要運行的程序放入run()函數之中就行。
但是,上述程序有一個問題,就是在調用函數out()時,可能在一個線程還沒有執行完時,就暫停,CPU轉而去執行另一個線程,導致另一個線程修改了這個線程的數據,導致輸出錯誤的結果。解決辦法就是在同一時刻就只能有一個線程訪問臨界資源,其他線程只能等待。
三、線程同步
在python中實現線程同步有多種方法
1. 線程鎖(Lock)
GIL(全局解釋器鎖)
GIL并不是Python的特性,它是在實現Python解析器(CPython)時所引入的一個概念,是為了實現不同線程對共享資源訪問的互斥,才引入了GIL。以下是原理圖:
我們對臨界資源加上鎖,這樣其他線程就無法訪問,直到這個線程完成操作,釋放線程鎖之后為止。如下代碼:
import threading
import time
#創建鎖
locka = threading.Lock()
lockb = threading.Lock()
class myThread(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
def run(self):
out()
def out():
print_time1()
print_time2()
def print_time1():
locka.acquire() #獲取鎖
print("Locka is acquired")
lockb.acquire() #獲取鎖
print("Lockb is acwuired")
lockb.release() #釋放鎖
locka.release() #釋放鎖
def print_time2():
lockb.acquire()
print("Lockb is acquired")
time.sleep(2)
locka.acquire()
print("Locka is acwuired")
locka.release()
lockb.release()
def main():
for i in range(50):
t = myThread()
t.start()
main()
在上面程序中,我們創建了兩個鎖locka和lockb,分別對臨界資源加鎖,這樣就可以讓同一時刻就只有一個線程執行,避免輸出錯誤結果。但是上述代碼還有一個錯誤,當第一個線程執行到函數print_time2()的time_sleep(2)時,需要獲取鎖locka,但是locka已經被第二個線程獲取,還沒有釋放,而且第二個線程也需要獲取lockb才能繼續運行,但是lockb已被第一個線程獲取,還沒有釋放,就這樣,兩個線程會一直等待,陷入死鎖。解決辦法是引入可重入鎖。
2.遞歸鎖(RLock)
遞歸鎖就是在同一個線程中可以獲取鎖所多次,不會陷入死鎖。但是在acquire()n次之后,需要release()n次。
import threading
import time
rlock = threading.RLock() #RLock本身有一個計數器,如果碰到acquire,那么計數器+1
#如果計數器大于0,那么其他線程無法查收,如果碰到release,計數器-1
class myThread(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
def run(self):
out()
def out():
print_time1()
print_time2()
def print_time1():
rlock.acquire() #獲取鎖
print("Locka is acquired")
rlock.acquire() #獲取鎖
print("Lockb is acwuired")
rlock.release() #釋放鎖
rlock.release() #釋放鎖
def print_time2():
rlock.acquire()
print("Lockb is acquired")
time.sleep(2)
rlock.acquire()
print("Locka is acwuired")
rlock.release()
rlock.release()
def main():
for i in range(50):
t = myThread()
t.start()
main()
三、Semaphore(信號量)
threading模塊里的Semaphore類實現了信號量對象,可用于控制獲取資源的線程數量。所具有的acquire()和release()方法,可以用with語句的上下文管理器。當進入時,將調用acquire()方法,當退出時,將調用release()。
import threading
import time
sem = threading.Semaphore(3) #設置線程并發數
class myThread(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
def run(self):
out()
def out():
print_time1()
print_time2()
def print_time1():
sem.acquire() #線程數減一
print("Locka is acquired")
print("Lockb is acwuired")
sem.release() #線程數加一
def print_time2():
sem.acquire() #線程數減一
print("Lockb is acquired")
print("Locka is acwuired")
sem.release() #線程數加一
def main():
for i in range(10):
t = myThread()
t.start()
main()
四、Condition(條件變量)
Condition(條件變量)通常與一個鎖關聯。需要在多個Contidion中共享一個鎖時,可以傳遞一個Lock/RLock實例給構造方法,否則它將默認生成一個RLock實例。
可以認為,除了Lock帶有的鎖定池外,Condition還包含一個等待池,池中的線程處于狀態圖中的等待阻塞狀態,直到另一個線程調用notify()/notifyAll()通知;得到通知后線程進入鎖定池等待鎖定。
Condition():
acquire(): 線程鎖
release(): 釋放鎖
wait(timeout): 線程掛起,并釋放鎖,直到收到一個notify通知或者超時(可選的,浮點數,單位是秒s)才會被喚醒繼續運行。wait()必須在已獲得Lock前提下才能調用,否則會觸發RuntimeError。
notify(n=1): 調用這個方法將從等待池挑選一個線程并通知,收到通知的線程將自動調用acquire()嘗試獲得鎖定(進入鎖定池);其他線程仍然在等待池中。調用這個方法不會釋放鎖定。使用前線程必須已獲得鎖定,否則將拋出異常。 最多喚醒n個等待的線程。
notifyAll(): 調用這個方法將通知等待池中所有的線程,這些線程都將進入鎖定池嘗試獲得鎖定。調用這個方法不會釋放鎖定。使用前線程必須已獲得鎖定,否則將拋出異常。
以下就以生產者消費者為例:
import threading
import time
import random
con = threading.Condition()
class Goods():
def __init__(self):
self.__goods = 0
def getgoods(self):
return self.__goods
def add(self):
self.__goods += 1
def sub(self):
self.__goods -= 1
def isEmpty(self):
if self.__goods <= 0:
return True
else:
return False
def isFull(self):
if self.__goods >= 10:
return True
else:
return False
class Producer(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
def run(self):
while True:
con.acquire()#獲取鎖
while goods.isFull(): #貨物滿了,需要消費才能生產,進入阻塞
con.wait()
goods.add()#生產一件貨物
print("生產一件貨物,總貨物數量為:", goods.getgoods())
con.notifyAll()#生產一件貨物后便喚醒所有正在等待的消費者
con.release()#釋放鎖
time.sleep(random.random())
class Consumer(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
def run(self):
while True:
con.acquire()#獲取鎖
while goods.isEmpty():#貨物消費完了,需要生產貨物,進入阻塞
con.wait()
goods.sub()#消費一件貨物
print("消費一件貨物,總貨物數量為:", goods.getgoods())
con.notifyAll()#消費一件貨物后便喚醒所有正在等待的生產者
con.release()#釋放鎖
time.sleep(random.random())
goods = Goods()
def main():
threads = []
#threads.append(Producer())
#threads.append(Comsumer())
for i in range(5):
threads.append(Producer())
for i in range(5):
threads.append(Consumer())
for th in threads:
th.start()
main()
五、同步隊列
讓我們考慮更復雜的一種場景:產品是各不相同的。這時只記錄一個數量就不夠了,還需要記錄每個產品的細節。很容易想到需要用一個容器將這些產品記錄下來。
Python的Queue模塊中提供了同步的、線程安全的隊列類,包括FIFO(先入先出)隊列Queue,LIFO(后入先出)隊列LifoQueue,和優先級隊列PriorityQueue。這些隊列都實現了鎖原語,能夠在多線程中直接使用。可以使用隊列來實現線程間的同步。
用FIFO隊列實現上述生產者與消費者問題的代碼如下:
import threading
import time
import random
import queue
q = queue.Queue() #創建一個線程同步隊列
class Producer(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
def run(self):
while True:
item = random.randint(0, 16)
while q.qsize() >= 10:
pass
q.put(item) #添加貨物
print("生產貨物%02d, 隊列大小:%02d" % (item, q.qsize()))
time.sleep(random.randint(0, 3))
class Consumer(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
def run(self):
while True:
item = q.get() #消費貨物,若為空,會阻塞
print("消費貨物%02d, 隊列大小:%02d" % (item, q.qsize()))
time.sleep(random.randint(0, 3))
def main():
threads = []
for i in range(5):
threads.append(Producer())
for i in range(5):
threads.append(Consumer())
for th in threads:
th.start()
main()
六、Event(事件)
python線程的事件用于主線程控制其他線程的執行,事件主要提供了三個方法wait、clear、set。
事件處理的機制:全局定義了一個“Flag”,如果“Flag”值為 False,那么當程序執行 event.wait 方法時就會阻塞,如果“Flag”值為True,那么event.wait 方法時便不再阻塞。
event.set() 設置標志位為True
event.clear() 清空標志位,標志位為False
event.wait() 等待設置標志位,阻塞
event.isSet() 判斷標志位是True還是False
下面就采用紅綠燈車通行的例子來示例:
import threading
import time
event = threading.Event()
def Lighter():
event.set()
count = 0
while True:
if count > 5 and count <= 10: #紅燈
event.clear() #清除標志位
elif count > 10: #變為綠燈
event.set() #重新設置標志位
count = 0
time.sleep(1)
count += 1
def Car(name):
while True:
if event.isSet(): #判斷標志位為True
print("light is green, %d is running" % name)
time.sleep(2)
else:
print("light is red, %d is waiting" % name)
event.wait() #阻塞,停車
def main():
light = threading.Thread(target=Lighter)
light.start()
threads = []
for i in range(5): #開五部車
threads.append(threading.Thread(target=Car, args=(i,)))
for car in threads:
car.start()
main()
這個程序將紅綠燈的情況來控制車的通行,即用紅綠燈這線程來控制車線程,達到一個線程控制多個線程的目的。
總結
以上是生活随笔為你收集整理的python多线程 不在main_Python多线程的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 耳机是怎么传输声音的_win7电脑耳机有
- 下一篇: log python_基于Python