from threading import Thread,Lock x = 0 def task(): global x for i in range(200000): x = x+1 # t1 的 x刚拿到0 保存状态 就被切了 # t2 的 x拿到0 进行+1 1 # t1 又得到运行了 x = 0 +1 1 # 这就产生了数据安全问题. if __name__ == '__main__': # 使用的是操做系统的原生线程. t1 = Thread(target=task) t2 = Thread(target=task) t3 = Thread(target=task) t1.start() t2.start() t3.start() t1.join() t2.join() t3.join() print(x)
import threading lock = threading.Lock() lock.acquire() # 锁头 ''' 公共数据的一系列操做 ''' lock.replease() # 释放锁
from threading import Thread,Lock x = 0 mutex = Lock() def task(): global x mutex.acquire() for i in range(200000): x = x+1 mutex.release() if __name__ == '__main__': # 使用的是操做系统的原生线程. t1 = Thread(target=task) t2 = Thread(target=task) t3 = Thread(target=task) t1.start() t2.start() t3.start() t1.join() t2.join() t3.join() print(x) #结果确定是600000,由原来的并发执行变成串行,牺牲了执行效率保证了数据安全
既然加锁会让运行变成串行,那么我在start以后当即使用join,就不用加锁了啊,也是串行的效果啊python
没错:在start以后马上使用jion,确定会将100个任务的执行变成串行,毫无疑问,最终n的结果也确定是0,是安全的,但问题是安全
start后当即join:任务内的全部代码都是串行执行的,而加锁,只是加锁的部分即修改共享数据的部分是串行的并发
单从保证数据安全方面,两者均可以实现,但很明显是加锁的效率更高.ui
所谓死锁:是指两个或两个以上的进程或线程在执行过程当中,因争夺资源而形成的一种互相等待的现象,若无外力做用,它们都将没法推动下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程,以下就是死锁操作系统
from threading import Thread,Lock mutex1 = Lock() mutex2 = Lock() import time class MyThreada(Thread): def run(self): self.task1() self.task2() def task1(self): mutex1.acquire() print(f'{self.name} 抢到了 锁1 ') mutex2.acquire() print(f'{self.name} 抢到了 锁2 ') mutex2.release() print(f'{self.name} 释放了 锁2 ') mutex1.release() print(f'{self.name} 释放了 锁1 ') def task2(self): mutex2.acquire() print(f'{self.name} 抢到了 锁2 ') time.sleep(1) mutex1.acquire() print(f'{self.name} 抢到了 锁1 ') # 程序在这里的时候会卡住 mutex1.release() print(f'{self.name} 释放了 锁1 ') mutex2.release() print(f'{self.name} 释放了 锁2 ') for i in range(3): t = MyThreada() t.start()
Thread-1 抢到了 锁1
Thread-1 抢到了 锁2
Thread-1 释放了 锁2
Thread-1 释放了 锁1
Thread-1 抢到了 锁2
Thread-2 抢到了 锁1线程
**:线程1在sleep1秒后拿到了锁头1,可是在线程1sleep的过程当中,操做系统已经去调度了其余的线程,而另外一个线程开始运行拿到了锁头2,而后当线程拿到锁2时sleep的时候,cpu又调度去执行了线程1,可是线程1这个时候已经释放锁1,须要锁2,可是锁2此时已经被线程2拿到尚未释放,而线程2接着往下执行的条件是拿到锁1,也就是能够理解为:code
线程1 拿到锁1,没释放递归
线程2 拿到锁2,没释放进程
线程1往下执行的条件是拿到锁2资源
线程2往下执行的条件是拿到锁1
这个时候,就出现了资源占用的问题,程序就会一直卡在那里,形成了死锁
解决的放法:递归锁
递归锁,在Python中为了支持在同一线程中屡次请求同一资源,python提供了可重入锁RLock。
这个RLock内部维护着一个Lock和一个counter变量,counter记录了acquire的次数,从而使得资源能够被屡次require。直到一个线程全部的acquire都被release,其余的线程才能得到资源。上面的例子若是使用RLock代替Lock,则不会发生死锁。
只有同一个线程下能够屡次acquire,acquire了几回就要release几回
from threading import Thread,Lock,RLock # 递归锁 在同一个线程内能够被屡次acquire # 如何释放 内部至关于维护了一个计数器 也就是说同一个线程 acquire了几回就要release几回 # mutex1 = Lock() # mutex2 = Lock() mutex1 = RLock() mutex2 = mutex1 import time class MyThreada(Thread): def run(self): self.task1() self.task2() def task1(self): mutex1.acquire() print(f'{self.name} 抢到了 锁1 ') mutex2.acquire() print(f'{self.name} 抢到了 锁2 ') mutex2.release() print(f'{self.name} 释放了 锁2 ') mutex1.release() print(f'{self.name} 释放了 锁1 ') def task2(self): mutex2.acquire() print(f'{self.name} 抢到了 锁2 ') time.sleep(1) mutex1.acquire() print(f'{self.name} 抢到了 锁1 ') mutex1.release() print(f'{self.name} 释放了 锁1 ') mutex2.release() print(f'{self.name} 释放了 锁2 ') for i in range(3): t = MyThreada() t.start() # 此时程序将会正常执行结束
关键字:Semaphore
Semaphore也是一个类,须要一个参数,参数是整型,表明能够同时能够有多个线程能够拿到多把锁
参数是几就表明一次能够执行多少个线程去拿几把锁
from threading import Thread,currentThread,Semaphore import time def task(): sm.acquire() print(f'{currentThread().name} 在执行') time.sleep(3) sm.release() sm = Semaphore(5) for i in range(15): t = Thread(target=task) t.start()