python之多線程(二)

共享變量

- 概念: 當多個線程同時訪問一個變量的時候,會產生共享變量的問題 

# 不啓用多線程,正常執行結果
import threading

sum = 0
loopSum = 100000


def myAdd():
    global sum, loopSum
    for i in range(1, loopSum):
        sum += 1


def myMinu():
    global sum, loopSum
    for i in range(1, loopSum):
        sum -= 1


if __name__ == '__main__':
    myAdd()
    print(sum)
    myMinu()
    print(sum)
99999
0
# 多線程執行兩個方法

import threading

sum = 0
loopSum = 100000


def myAdd():
    global sum, loopSum
    for i in range(1, loopSum):
        sum += 1


def myMinu():
    global sum, loopSum
    for i in range(1, loopSum):
        sum -= 1


if __name__ == '__main__':
    print("Stating ....{0}".format(sum))
    # 啓用多線程實例,查看執行結果
    t1 = threading.Thread(target=myAdd, args=())
    t2 = threading.Thread(target=myMinu, args=())
    t1.start()
    t2.start()
    t1.join()
    t2.join()
    print("Done .... {0}".format(sum))
Stating ....0
Done .... -31331

解決方案: 鎖, 

  • 鎖(LOCK): 
    • 是一個標誌,表示一個線程在佔用一些資源
    • 使用方法: 
      • 上鎖
      • 使用共享資源
      • 釋放鎖
    • 瑣誰: 
      • 那個資源需要多線程共享,鎖那個
    • 理解鎖: 
      • 鎖其實是一個令牌,並且這個令牌只有一個,訪問共享資源時需要去申請這個令牌,只有申請到令牌的線程才能操作共享資源,操作完成後,需將令牌歸還
import threading

sum = 0
loopSum = 100000
# 定義鎖
lock = threading.Lock()


def myAdd():
    global sum, loopSum
    for i in range(1, loopSum):
        # 加鎖,申請鎖
        lock.acquire()
        sum += 1
        # 釋放鎖
        lock.release()


def myMinu():
    global sum, loopSum
    for i in range(1, loopSum):
        # 加鎖,申請鎖
        lock.acquire()
        sum -= 1
        # 釋放鎖
        lock.release()


if __name__ == '__main__':
    print("Stating ....{0}".format(sum))
    # 啓用多線程實例,查看執行結果
    t1 = threading.Thread(target=myAdd, args=())
    t2 = threading.Thread(target=myMinu, args=())
    t1.start()
    t2.start()
    t1.join()
    t2.join()
    print("Done .... {0}".format(sum))
Stating ....0
Done .... 0

 線程安全問題: 
- 如果一個資源/變量,它對於多線程來講,不用加鎖也不會引起任何問題,則稱爲線程安全。 
- 線程不安全變量類型:list,set,dict 
- 線程安全變量類型:queue 

  • 生產者消費者問題 
    • 一個模型,用來搭建消息隊列
    • queue 是一個用來存放變量的數據結構,特點是先進先出,內部元素排隊,可以理解成一個特殊的list

 

import threading
import time
import queue


# python2
# from Queue import Queue

# python3
class Produer(threading.Thread):
    def run(self):
        global queue
        count = 0
        while True:
            if queue.qsize() < 1000:
                for i in range(100):
                    count = count + 1
                    msg = "生成產品" + str(count)
                    queue.put(msg)
                    print(msg)
            time.sleep(0.5)


class Consumer(threading.Thread):
    def run(self):
        global queue
        while True:
            if queue.qsize() > 100:
                for i in range(3):
                    msg = self.name + "消費了" + queue.get()
                    print(msg)
            time.sleep(1)


if __name__ == "__main__":
    queue = queue.Queue()
    for i in range(500):
        queue.put("初始產品:"+str(i))
    for i in range(2):
        p = Produer()
        p.start()
    for i in range(5):
        c = Consumer()
        c.start()

死鎖

import threading
import time

lock_1 = threading.Lock()
lock_2 = threading.Lock()

def funca():
    print("A 函數啓動....")
    print("A 申請了鎖1...")
    lock_1.acquire()
    print("A 等待申請鎖2...")
    time.sleep(2)
    lock_2.acquire()
    print("A 釋放了鎖2...")
    lock_2.release()
    print("A 釋放了鎖1")
    lock_1.release()
    print("A 函數執行結束...")


def funcb():
    print("B 函數啓動...")
    print("B 函數申請鎖2...")
    lock_2.acquire()
    print("B 函數等待申請鎖1...")
    time.sleep(4)
    print("B 函數申請申請鎖1...")
    lock_1.acquire()

    print("B 函數釋放鎖1...")
    lock_1.release()
    print("B 函數釋放鎖2...")
    lock_2.release()
    print("B 函數執行結束...")


if __name__ == '__main__':
    t1 = threading.Thread(target=funca, args=())
    t2 = threading.Thread(target=funcb, args=())
    t1.start()
    t2.start()
    t1.join()
    t2.join()
  • 鎖的等待時間
import threading
import time

lock_1 = threading.Lock()
lock_2 = threading.Lock()


def funca():
    print("A 函數啓動....")
    print("A 申請了鎖1...")
    lock_1.acquire(timeout=4)
    print("A 等待申請鎖2...")
    time.sleep(2)
    res = lock_2.acquire(timeout=2)
    if res:
        print("A 得到了鎖2...")
        lock_2.release()
        print("A 釋放了鎖2...")
    else:
        print("A 沒有申請到鎖2...")
    print("A 釋放了鎖1")
    lock_1.release()
    print("A 函數執行結束...")


def funcb():
    print("B 函數啓動...")
    print("B 函數申請鎖2...")
    lock_2.acquire()
    print("B 函數等待申請鎖1...")
    time.sleep(4)
    print("B 函數申請申請鎖1...")
    lock_1.acquire()

    print("B 函數釋放鎖1...")
    lock_1.release()
    print("B 函數釋放鎖2...")
    lock_2.release()
    print("B 函數執行結束...")


if __name__ == '__main__':
    t1 = threading.Thread(target=funca, args=())
    t2 = threading.Thread(target=funcb, args=())
    t1.start()
    t2.start()
    t1.join()
    t2.join()
  • semphore 
    - 允許一個資源最多由幾個線程同時使用
import threading
import time

# 參數定義同一個資源最多幾個線程同時使用

semphore = threading.Semaphore(3)


def func():
    if semphore.acquire():
        for i in range(5):
            print(threading.currentThread().getName() + "get semphore")
            time.sleep(15)
            semphore.release()
            print(threading.currentThread().getName() + "release semphore")


for i in range(8):
    t1 = threading.Thread(target=func)
    t1.start()
  •  threading.Timer 
    -Timer : 是利用多線程在指定時間後啓動一個功能
import threading
import time

def func():
    print("I am body")
    time.sleep(4)
    print("bay")


if __name__ == "__main__":
    t1 = threading.Timer(6, func)
    t1.start()
    i = 0
    while i < 6:
        print("{0}..........".format(i))
        time.sleep(2)
        i += 1
0..........
1..........
2..........
I am body
3..........
4..........
bay
5..........
  •  可重入鎖: 
    - 可以被一個線程多次申請 
    - 主要解決遞歸調用的時候,需要申請鎖的情況(遞歸調用多次申請鎖)
import threading
import time

lock = threading.RLock()


class MyThread(threading.Thread):
    def run(self):
        global num
        time.sleep(1)

        if lock.acquire(1):
            num += 1
            msg = self.name + 'set num to ' + str(num)
            print(msg)
            lock.acquire()
            lock.release()
            lock.release()


num = 0

def mytherad():
    for i in range(8):
        t1 = MyThread()
        t1.start()


if __name__ == '__main__':
    mytherad()
Thread-77set num to 1
Thread-78set num to 2
Thread-79set num to 3
Thread-80set num to 4
Thread-81set num to 5
Thread-82set num to 6
Thread-83set num to 7
Thread-84set num to 8

 

發佈了36 篇原創文章 · 獲贊 24 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章