线程与线程之间数据是共享的
from threading import Thread
def func(lst, name):
lst.append(66)
print(name, lst)
if __name__ == '__main__':
lst = [1, 2]
t1 = Thread(target=func, args=(lst, "线程1"))
t2 = Thread(target=func, args=(lst, "线程2"))
t1.start()
t2.start()
t1.join()
t2.join()
print("主线程结束", lst)
执行结果:
线程1 [1, 2, 66]
线程2 [1, 2, 66, 66]
主线程结束 [1, 2, 66, 66]
带有global的线程与线程之间的数据共享情况
from threading import Thread
def func(name):
global n
print(f"{name}开始,n={n}")
n = 0
print(f"{name}结束,n={n}")
if __name__ == '__main__':
n = 100
t1 = Thread(target=func, args=("线程1",))
t1.start()
t1.join()
print(f"主线程结束,n={n}")
执行结果:
线程1开始,n=100
线程1结束,n=0
主线程结束,n=0
接下来看一个由于线程之间的数据是共享而引发问题
from threading import Thread
def func(name):
print(f"{name}开始")
global n
for i in range(100000):
n += 1
print(f"{name}结束,n={n}")
if __name__ == '__main__':
n = 0
t1 = Thread(target=func, args=("线程1",))
t2 = Thread(target=func, args=("线程2",))
t1.start()
t2.start()
t1.join()
t2.join()
print(f"主线程结束,n={n}")
执行结果:
线程1开始
线程2开始
线程1结束,n=165337
线程2结束,n=147914
主线程结束,n=147914
最终的结果不是我们预期的200000
解决线程与线程之间数据共享导致的问题:加锁
from threading import Thread, Lock
def func(name, lock: Lock): # 传递lock,后面添加: Lock表示是Lock类型的,是为了上锁和释放锁的时候会有方法提示功能
print(f"{name}开始")
global n
for i in range(100000):
lock.acquire() # 上锁
n += 1
lock.release() # 释放锁
print(f"{name}结束,n={n}")
if __name__ == '__main__':
lock = Lock() # 创建一把锁
n = 0
t1 = Thread(target=func, args=("线程1", lock))
t2 = Thread(target=func, args=("线程2", lock))
t1.start()
t2.start()
t1.join()
t2.join()
print(f"主线程结束,n={n}")
执行结果:
线程1开始
线程2开始
线程1结束,n=167229
线程2结束,n=200000
主线程结束,n=200000
只看最终结果就行,因为线程1和线程2是并行执行的。
线程与线程之间数据共享(加锁)
from threading import Thread, Lock
n = 0
def func(lock, name):
lock.acquire()
global n
for i in range(1000000):
n += 1
print("%s拿到了锁" % name)
lock.release()
if __name__ == '__main__':
lock = Lock() # 线程锁
t1 = Thread(target=func, args=(lock, "线程1"))
t2 = Thread(target=func, args=(lock, "线程2"))
t3 = Thread(target=func, args=(lock, "线程3"))
t1.start()
t2.start()
t3.start()
t1.join()
t2.join()
t3.join()
print(n)
运行结果:
线程1拿到了锁
线程2拿到了锁
线程3拿到了锁
3000000
死锁现象
from threading import Thread, Lock
def func(name, lock: Lock): # 传递lock,后面添加: Lock表示是Lock类型的,是为了上锁和释放锁的时候会有方法提示功能
print(f"{name}开始")
lock.acquire() # 上锁
lock.acquire() # 上锁
print(f"{name}被锁住了")
lock.release() # 释放锁
lock.release() # 释放锁
print(f"{name}结束")
if __name__ == '__main__':
lock = Lock() # 创建一把锁
t1 = Thread(target=func, args=("线程1", lock))
t2 = Thread(target=func, args=("线程2", lock))
t1.start()
t2.start()
t1.join()
t2.join()
print(f"主线程结束")
执行结果:
线程1开始
线程2开始
程序一直执行不完,因为程序被锁住了。
解决死锁--->使用递归锁RLock
from threading import Thread, RLock
def func(name, lock: RLock): # 传递lock,后面添加: Lock表示是Lock类型的,是为了上锁和释放锁的时候会有方法提示功能
print(f"{name}开始")
lock.acquire() # 上锁
lock.acquire() # 上锁
print(f"{name}被锁住了")
lock.release() # 释放锁
lock.release() # 释放锁
print(f"{name}结束")
if __name__ == '__main__':
lock = RLock() # 创建一把锁
t1 = Thread(target=func, args=("线程1", lock))
t2 = Thread(target=func, args=("线程2", lock))
t1.start()
t2.start()
t1.join()
t2.join()
print(f"主线程结束")
执行结果:
线程1开始
线程1被锁住了
线程1结束
线程2开始
线程2被锁住了
线程2结束
主线程结束
完美解决死锁的问题,切记锁可以保证数据的安全性,但是效率会降低,相当于把并行改为了串行。
死锁可以想成两个人围着桌子吃饭,两个人的中间各放着一根的筷子,两根筷子在一起才能吃饭,此时,两个人都想吃饭,但是都抓着筷子不放开,结果谁也吃不上饭,就僵死在那了。