多线程加锁

本文介绍了Python中多线程加锁的概念,包括加锁的位置、加锁类`LOCK`的工作原理、加锁的作用以及可重入锁`RLock`的特性。还提到了条件变量`condion`作为线程间同步的一种机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


1. 多线程加锁

在多线程加锁的过程中,要注意如下两个核心关键点:

  • 加锁的位置,加锁的位置代表了一旦其中某一线程获取锁后,其他线程就会阻塞到此位置,当加锁的线程执行完毕释放锁后,其他线程会根据阻塞时的位置继续向向执行。
  • 加锁边界处理, 在到达边界时,一旦某一线程完成任务后,其他阻塞的线程就不能继续完成任务,要考虑其他线程要退出任务。
  • 防止死锁,正常情况下cpu执行一个锁要从加锁到释放锁这么一个原子性操作,若在加锁后还为解锁时,就退出任务,此时就变为死锁,自己和其他都一致会阻塞
    举例说明:
import time
from threading import Thread,Lock,currentThread
import logging

FORMAT = "%(asctime)s %(threadName)s %(thread)d %(message)s"
# logging.basicConfig(format=FORMAT,level=logging.INFO)
logging.basicConfig(format=FORMAT,level=logging.INFO)

cups = []
lock = Lock()
def worker(count=10):
    logging.info("I'am working for CPU.")
    flag = False
    while not flag:
        #如果len(cups)=999,线程1进入循环在此枷锁,
        # 其他线程也进入此循环阻塞到此位置,若线程1执行完后,其他线程
        #也要枪锁,因此要判断是否要cups.append(1),若已经达到1000,则跳过;
        lock.acquire()
        time.sleep(0.001)
        print(len(cups))
        if len(cups) >= 1000:
            flag = True
            #若此位置加break,尚未解锁就结束,形成死锁
            #break
        #达到100时,阻塞的线程进来后,要根据flag值判断是否在添加,若没有
        #判断时,其他阻塞的线程进来依然会添加,因此会有错误
        #cups.append(1)
        #正确做法
        if not flag:
            cups.append(1)
        print(currentThread())
        lock.release()
    logging.info('I finished. cups = {}'.format(len(cups)))

for _ in range(10):
    Thread(target=worker,args=(1000,)).start()

2. 加锁类LOCK

acquire(blocking=Ture,timeout=-1)

  • 默认情况下是阻塞状态,当无法获取到锁时,会一直处于阻塞状态,当然也可以设置timeout超时时间,到达超时时间立马退出
  • 若blocking=False时,当没有获取到锁时,立马退出,无须阻塞等待

release

  • 释放锁,可以从任何线程调用释放
  • 已上锁的锁,会被重置为unlocked未上锁的锁上调用,抛出RuntimeError异常

3. 加锁作用

在多线程中,多线程同时处理一个公共变量时,线程A和线程B,如
当x = 0, x = x + 1时,尚未执行完成,切换另外一个线程x = x +1,本来应该是2,但是因为第一个线程x= x+ 1执行一半,因此x再次变为1

import threading
from threading import Thread,Lock
import time

class Counter:
    def __init__(self):
        self._val = 0

    @property
    def value(self):
        return self._val

    def inc(self):
        self._val += 1

    def dec(self):
        self._val -= 1

def run(c,count=100):
    for _ in range(count):
        for i in range(-50,50):
            if i < 0:
                c.dec()
            else:
                c.inc()

c = Counter()
c1 = 100
c2 = 100
for i in range(c1):
    Thread(target=run,args=(c,c2)).start()
print(c.value)

4. 可重入锁RLock

可重入锁当线程A中获取一次或多次锁lock,同时将同一把锁lock传递到另外一个线程B中,当线程A中不释放这把锁时,线程B中就因为这把锁无法使用,线程B就处于阻塞状态。
如:你拿一把锁链,把自行车多次上锁,但是你又想把这把锁锁另外一个自行车,肯定无法使用,因为锁还没有解开,所以另外一个自行车只能处于等待状态。

import threading
import time

lock = threading.RLock()
lock.acquire()
def worker(lock):
    print("blocking ...")
    lock.acquire()
    print(threading.current_thread().ident)
    print()
    print("end blocking ...")

t = threading.Thread(target=worker,args=(lock,))
t.start()
print(lock)
t.join()
print("end")
#运行结果
blocking ...
<locked _thread.RLock object owner=8724 count=1 at 0x000001941A851C88>
##阻塞状态

5. condion

condion是条件通知机制,主要解决的问题是:生产者和消防者速度不匹配的问题,有了condion后,生产者生产完后,通知消费者来消费,这样更加高效的完成任务

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值