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

Posted xichenhome

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了120 python程序中的线程操作-锁相关的知识,希望对你有一定的参考价值。

一、同步锁

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,就不用加锁了啊,也是串行的效果啊

没错:在start之后立刻使用jion,肯定会将100个任务的执行变成串行,毫无疑问,最终n的结果也肯定是0,是安全的,但问题是

start后立即join:任务内的所有代码都是串行执行的,而加锁,只是加锁的部分即修改共享数据的部分是串行的

单从保证数据安全方面,二者都可以实现,但很明显是加锁的效率更高.

二、死锁与递归锁

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

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,也就是可以理解为:

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

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

三、信号量

关键字: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()

以上是关于120 python程序中的线程操作-锁的主要内容,如果未能解决你的问题,请参考以下文章

Python程序中的线程操作-锁

java 知识点突击-(111-120)

Python中的线程锁

Python进阶_进程与线程中的lock(互斥锁递归锁信号量)

Python网络编程(进程通信信号线程锁多线程)

Python 线程锁