python多線程中的共享全局變量的鎖機制

一、Lock版鎖機制

多線程都是在同一個進程中運行的。因此在進程中的全局變量所有線程都是可共享的。這就造成了一個問題,因爲線程執行的順序都是無序的,有可能會造成數據錯誤,代碼如下:

#!/usr/bin/env python
#-*- coding:utf-8 -*-
import threading
VALUE = 0
def add_value():
    global  VALUE
    for i in range(10000):
        VALUE += 1
    print('VALUE值爲:', VALUE)

def main():
    for x in range(2):
        t  = threading.Thread(target=add_value)
        t.start()

if __name__ == '__main__':
    main()

因此需要在修改全局變量的時候給它加個鎖,改善後代碼:
示例一:增加數字

#!/usr/bin/env python
#-*- coding:utf-8 -*-
# acquire:上鎖
# release:解鎖

import threading
VALUE = 0
Lock = threading.Lock()

def add_value():
    global  VALUE
    Lock.acquire()
    for i in range(10000):
        VALUE += 1
    print('VALUE值爲:', VALUE)
    Lock.release()

def main():
    for x in range(2):
        t  = threading.Thread(target=add_value)
        t.start()

if __name__ == '__main__':
    main()

示例二:生產者和消費者模型
模型:生產者生產 ===> 全局變量 ===> 消費者消費變量

#!/usr/bin/env python
#-*- coding:utf-8 -*-
import threading,random,time
VALUE = 1000
Lock = threading.Lock()

class Product(threading.Thread):
    def run(self):
        global VALUE
        while True:
            m = random.randint(100,500)
            Lock.acquire()
            VALUE += m
            print('%s 生成了數字 %s ,現在數字大小爲 %s ' %(threading.current_thread(),m,VALUE))
            Lock.release()
            time.sleep(0.5)

class Consumer(threading.Thread):
    def run(self):
        global VALUE
        while True:
            m = random.randint(200,600)
            Lock.acquire()
            if VALUE >= m:
                VALUE -= m
                print('%s 消費了數字 %s ,現在數字大小爲 %s ' %(threading.current_thread(),m,VALUE))
            else:
                print('數字 %s 太小' %VALUE)
            Lock.release()
            time.sleep(0.5)


def main():
    for x in range(3):
        t = Consumer(name='消費者線程%d' %(x))
        t.start()

    for x in range(5):
        t = Product(name='生產者線程%d' %(x))
        t.start()

if __name__ == '__main__':
    main()

二、Condition版鎖機制

由於上鎖是一個很耗費CPU資源的行爲,因此 Lock鎖機制 方式不是很友好。還有一種是通過 threading.Condition 來實現。 threading.Condition 可以在沒有數據的時候處於阻塞等待狀態,一旦有了合適的數據,可以使用 notify 相關的函數來通知其他等待的線程。這樣可以不用做一些無用的上鎖和解鎖的操作,提高程序的性能。,threading.Condition類似threading.Lock,可以在修改全局數據的時候進行上鎖,也可以在修改完畢後進行解鎖。

acquire:上鎖。
release:解鎖。
wait:將當前線程處於等待狀態,並且會釋放鎖。可以被其他線程使用notify和notify_all函數喚醒。被喚醒後會繼續等待上鎖,上鎖後繼續執行下面的代碼。
notify:通知某個正在等待的線程,默認是第1個等待的線程。
notify_all:通知所有正在等待的線程。notify和notify_all不會釋放鎖。並且需要在release之前調用

#!/usr/bin/env python
#-*- coding:utf-8 -*-
# 生產者和消費者模型

import threading,random,time
VALUE = 1000
cLock = threading.Condition()

class Product(threading.Thread):
    def run(self):
        global VALUE
        while True:
            m = random.randint(100,500)
            cLock.acquire()
            VALUE += m
            print('%s 生成了數字 %s ,現在數字大小爲 %s ' %(threading.current_thread(),m,VALUE))
            cLock.notify_all()  # 通知wait()
            cLock.release()
            time.sleep(0.5)

class Consumer(threading.Thread):
    def run(self):
        global VALUE
        while True:
            m = random.randint(200,600)
            cLock.acquire()
            if VALUE >= m:
                VALUE -= m
                print('%s 消費了數字 %s ,現在數字大小爲 %s ' %(threading.current_thread(),m,VALUE))
            else:
                print('數字 %s 太小' %VALUE)
                cLock.wait()   # 全局值太小了,那就等待,直到通知時候再去排隊
            cLock.release()
            time.sleep(0.5)


def main():
    for x in range(3):
        t = Consumer(name='消費者線程%d' %(x))
        t.start()

    for x in range(2):
        t = Product(name='生產者線程%d' %(x))
        t.start()

if __name__ == '__main__':
    main()




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