120 python程序中的线程操做-锁

1、同步锁

1.1 多个线程抢占资源的状况

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)

1.2 对公共数据进行锁操做

import threading
lock = threading.Lock()
lock.acquire() # 锁头
'''
公共数据的一系列操做 
'''
lock.replease() # 释放锁

1.3 同步锁的引用

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

2、死锁与递归锁

所谓死锁:是指两个或两个以上的进程或线程在执行过程当中,因争夺资源而形成的一种互相等待的现象,若无外力做用,它们都将没法推动下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程,以下就是死锁操作系统

2.1 死锁实例

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几回

2.2 递归锁解决上述死锁问题

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()

# 此时程序将会正常执行结束

3、信号量

关键字: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()
相关文章
相关标签/搜索