Python高級編程——12.(2)系統編程之多線程

一、多線程與多進程的效率對比
(1)多線程
from threading import Thread,current_thread  #  多線程    current_thread 當前線程
import time
def run(msg):
        print("這個是獨立線程運行的代碼",msg)
        time.sleep(1)
if __name__ == '__main__':
    new_time = time.time()
    print("++++++++++  main start +++++++++++")
    for x in range(10):
        t1 = Thread(target=run,args=("這個是參數%s"%x,))
        t1.start()
    print("==========  main end ==========", current_thread().getName(),time.time()-new_time)
                                               # current_thread().getName()可以得到當前主線程的名稱
'''                                             
運行結果:
++++++++++  main start +++++++++++
這個是獨立線程運行的代碼 這個是參數0
這個是獨立線程運行的代碼 這個是參數1
這個是獨立線程運行的代碼 這個是參數2
這個是獨立線程運行的代碼 這個是參數3
這個是獨立線程運行的代碼 這個是參數4
這個是獨立線程運行的代碼 這個是參數5
這個是獨立線程運行的代碼 這個是參數6
這個是獨立線程運行的代碼 這個是參數7
這個是獨立線程運行的代碼 這個是參數8
這個是獨立線程運行的代碼 這個是參數9
==========  main end ========== MainThread,0.0010006427764892578
'''
(2)多進程

from multiprocessing import Process   # 多進程
import time
def run(msg):
        print("這個是獨立線程運行的代碼",msg)
        time.sleep(1)
if __name__ == '__main__':
    new_time = time.time()
    print("++++++++++  main start +++++++++++")
    for x in range(10):
        t1 = Process(target=run,args=("這個是參數%s"%x,))
        t1.start()
    print("==========  main end ==========",time.time()-new_time)
'''
運行結果:
++++++++++  main start +++++++++++
==========  main end ========== 0.2971487045288086
這個是獨立線程運行的代碼 這個是參數0
這個是獨立線程運行的代碼 這個是參數2
這個是獨立線程運行的代碼 這個是參數1
這個是獨立線程運行的代碼 這個是參數9
這個是獨立線程運行的代碼 這個是參數3
這個是獨立線程運行的代碼 這個是參數5
這個是獨立線程運行的代碼 這個是參數8
這個是獨立線程運行的代碼 這個是參數4
這個是獨立線程運行的代碼 這個是參數7
這個是獨立線程運行的代碼 這個是參數6
'''
二、多線程,類的實現方式

from threading import Thread
class Team(Thread):
    def __init__(self, name):
        super().__init__(name=name)  # 繼承自Thread

    def run(self):
        for i in range(3):
            print("這是類中的子線程")

if __name__ == '__main__':
    print("+++++++++++  main  start ++++++++++++")
    t = Team("麗麗")
    t.start()
    print("==========  main  end  ============")
'''
運行結果:
+++++++++++  main  start ++++++++++++
這是類中的子線程                                                           # 可以看出多線程的運行速度幾乎超過了主線程,這在多進程中是不會出現的
==========  main  end  ============
這是類中的子線程
這是類中的子線程
這是類中的子線程
'''
 三、多線程共享全局變量

我們前面已經說過了,多進程之間的數據時獨立的,各自都有一份,即便是全局變量也不共享,那麼一個進程中的多線程之間的全局變量呢?注意:多線程之間的全局數據數是共享的,因爲多線程是在一個進程,數據是互相可以訪問的,案例如下:

import threading import time num = 100 def task1():     global num     for in range(3):         num += 1     print("run1中的num=",num) def task2():     print("run2中的num=",num) def run():     t1 = threading.Thread(target=task1)     t1.start()     time.sleep(1)     t2 = threading.Thread(target=task2)     t2.start() if __name__ == '__main__':     run()

由此可知,一個進程間的多線程(下面開始統一叫多線程,因爲我們一直說的多線程就是一個進程下的多線程)的全局數據是共享的。

四、互斥鎖(metux)

當多個線程⼏乎同時修改某⼀個共享數據的時候,需要進⾏同步控制線程同步能夠保證多個線程安全訪問競爭資源,最簡單的同步機制是引⼊斥鎖

互斥鎖爲資源引⼊⼀個狀態:鎖定/⾮鎖定。

某個線程要更改共享數據時,先將其鎖定,此時資源的狀態爲“鎖定”,其他線程不能更改;直到該線程釋放資源,將資源的狀態變成“⾮鎖定”,其他的線程才能再次鎖定該資源。互斥鎖保證了每次只有⼀個線程進⾏寫⼊操作,從⽽保證了多線程情況下數據的正確性。

from threading import *

num = 0
def run1(lock):
    global num
    for x in range(1000000):
        lock.acquire( )  # 將程序鎖定
        num += 1
        lock.release( ) # 解鎖
    print(num)

def run2(lock):
    global num
    for x in range(1000000):
        lock.acquire()   # 將程序鎖定
        num += 1
        lock.release()    # 解鎖
    print(num)
if __name__ == '__main__':
    lock = Lock()  # 創建互斥鎖,用於解決非線程安全問題
    t1 = Thread(target=run1,args=(lock,))
    t1.start()
    t2 = Thread(target=run2,args=(lock,))
    t2.start()

# 運行結果:1993755
#                  2000000
如果不加互斥鎖,運行的結果應該會小於2000000,可參考12.系統編程(多進程和多線程)

五、同步(藉助於互斥鎖實現的)
from threading import Thread,Lock
import time
myLock1 = Lock()
myLock2 = Lock()
myLock3 = Lock()
# 通過加鎖來實現一個同步案例,同步即多線程中的協同步調
def run1():
    while True:
        if myLock1.acquire():
            print("run1")
            myLock2.release()
            time.sleep(1)

def run2():
    while True:
        if myLock2.acquire():
            print("run2")
            myLock3.release()
            time.sleep(1)

def run3():
    while True:
        if myLock3.acquire():
            print("run3")
            myLock1.release()
            time.sleep(1)
if __name__ == '__main__':
    myLock2.acquire()
    myLock3.acquire()
    t1 = Thread(target=run1)
    t1.start()
    t3 = Thread(target=run3)
    t3.start()
    t2 = Thread(target=run2)
    t2.start()
# 運行結果:
# run1
#run2
# run3
# run1
#run2
#run3
......

六、死鎖

在線程間共享多個資源的時候,如果兩個線程分別佔有⼀部分資源並且同時等待對的資源,就會造成死鎖。

儘管死鎖很少發⽣,但⼀旦發⽣就會造成應⽤的停⽌響應。

from threading import Thread,Lock
import  time

# 申請兩個全局鎖
myLock1= Lock()
myLock2= Lock()
def run1():
    print("第一個子線程運行 ")
    if myLock1.acquire(timeout=2):  #  等待2s
        print("lock1已經加鎖了")
        time.sleep(1)  # 人爲的代碼停止一下
        if myLock2.acquire(): # 返回布爾值,表示加鎖成功,第一次返回布爾值,第二次卡死
            print("第一個執行了嗎?")
        myLock2.release()
    myLock1.release()
def run2():
   print("第二個子線程運行了")
   if myLock2.acquire():
       print("lock2已經加鎖了")
       time.sleep(1)
       if myLock1.acquire():
           print("第二個執行了嗎?")
       myLock1.release()
   myLock2.release()
if __name__ == '__main__':
    t1 = Thread(target=run1)
    t1.start()
    t2 = Thread(target=run2)
    t2.start()

# 運行結果:
#     第一個子線程運行
#     lock1已經加鎖了
#     第二個子線程運行了
#     lock2已經加鎖了
可以用遞歸加鎖的方式解決死鎖現象:rlock(),或者銀行家算法

七、隊列

八、Threadlocal

在多線程環境下,每個線程都有⾃⼰的數據。⼀個線程使⽤⾃⼰的局部變量⽐使⽤全局變量好,

因爲局部變量只有線程⾃⼰能看見,不會影響其他線程,⽽全局變量的修改必須加鎖。


from threading import Thread, local

mylocal = local()  # new一個local對象

def printMsg():
    print(mylocal.name, mylocal.age, mylocal.gender)

def speak():
    print("%s說了一句話" % mylocal.name)

def run1():
    # 自身函數中需要的參數綁定到本地線程Threadlocal    mylocal.name = "張三"
    mylocal.age = 18
    mylocal.gender = ""
    printMsg()
    speak()

def run2():
    mylocal.name = "李四"
    mylocal.age = 25
    mylocal.gender = ""
    printMsg()
    speak()

if __name__ == '__main__':
    t1 = Thread(target=run1)
    t2 = Thread(target=run2)
    t1.start()
    t2.start()
    #  運行結果:   張三 18     #             張三說了一句話
    #             李四 25     #             李四說了一句話
















發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章