共享變量
- 概念: 當多個線程同時訪問一個變量的時候,會產生共享變量的問題
# 不啓用多線程,正常執行結果
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