Python中threading库实现线程锁与释放锁


Posted in Python onMay 17, 2021

控制资源访问

前文提到threading库在多线程时,对同一资源的访问容易导致破坏与丢失数据。为了保证安全的访问一个资源对象,我们需要创建锁。

示例如下:

import threading
import time

class AddThread():
    def __init__(self, start=0):
        self.lock = threading.Lock()
        self.value = start

    def increment(self):
        print("Wait Lock")
        self.lock.acquire()
        try:
            print("Acquire Lock")
            self.value += 1
            print(self.value)
        finally:
            self.lock.release()

def worker(a):
    time.sleep(1)
    a.increment()

addThread = AddThread()
for i in range(3):
    t = threading.Thread(target=worker, args=(addThread,))
    t.start()

运行之后,效果如下:

Python中threading库实现线程锁与释放锁

acquire()会通过锁进行阻塞其他线程执行中间段,release()释放锁,可以看到,基本都是获得锁之后才执行。避免了多个线程同时改变其资源对象,不会造成混乱。

判断是否有另一个线程请求锁

要确定是否有另一个线程请求锁而不影响当前的线程,可以设置acquire()的参数blocking=False。

示例如下:

import threading
import time

def worker2(lock):
    print("worker2 Wait Lock")
    while True:
        lock.acquire()
        try:
            print("Holding")
            time.sleep(0.5)
        finally:
            print("not Holding")
            lock.release()
        time.sleep(0.5)

def worker1(lock):
    print("worker1 Wait Lock")
    num_acquire = 0
    value = 0
    while num_acquire < 3:
        time.sleep(0.5)
        have_it = lock.acquire(blocking=False)
        try:
            value += 1
            print(value)
            print("Acquire Lock")
            if have_it:
                num_acquire += 1
        finally:
            print("release Lock")
            if have_it:
                lock.release()

lock = threading.Lock()
word2Thread = threading.Thread(
    target=worker2,
    name='work2',
    args=(lock,)
)
word2Thread.start()
word1Thread = threading.Thread(
    target=worker1,
    name='work1',
    args=(lock,)
)
word1Thread.start()

运行之后,效果如下:

Python中threading库实现线程锁与释放锁

这里,我们需要迭代很多次,work1才能获取3次锁。但是尝试了很8次。

with lock

前文,我们通过lock.acquire()与lock.release()实现了锁的获取与释放,但其实我们Python还给我们提供了一个更简单的语法,通过with lock来获取与释放锁。

示例如下:

import threading
import time

class AddThread():
    def __init__(self, start=0):
        self.lock = threading.Lock()
        self.value = start

    def increment(self):
        print("Wait Lock")
        with self.lock:
            print("lock acquire")
            self.value += 1
            print(self.value)
        print("lock release")

def worker(a):
    time.sleep(1)
    a.increment()

addThread = AddThread()
for i in range(3):
    t = threading.Thread(target=worker, args=(addThread,))
    t.start()

这里,我们只是将最上面的例子改变了一下。效果如下:

Python中threading库实现线程锁与释放锁

需要注意的是,正常的Lock对象不能请求多次,即使是由同一个线程请求也不例外。如果同一个调用链中的多个函数访问一个锁,则会发生意外。如果期望在同一个线程的不同代码需要重新获得锁,那么这种情况下使用RLock。

同步线程

Condition

在实际的操作中,我们还可以使用Condition对象来同步线程。由于Condition使用了一个Lock,所以它可以绑定到一个共享资源,允许多个线程等待资源的更新。

示例如下:

import threading
import time

def consumer(cond):
    print("waitCon")
    with cond:
        cond.wait()
        print('获取更新的资源')

def producer(cond):
    print("worker")
    with cond:
        print('更新资源')
        cond.notifyAll()

cond = threading.Condition()
t1 = threading.Thread(name='t1', target=consumer, args=(cond,))
t2 = threading.Thread(name='t2', target=consumer, args=(cond,))
t3 = threading.Thread(name='t3', target=producer, args=(cond,))
t1.start()
time.sleep(0.2)
t2.start()
time.sleep(0.2)
t3.start()

运行之后,效果如下:

Python中threading库实现线程锁与释放锁

这里,我们通过producer线程处理完成之后调用notifyAll(),consumer等线程等到了它的更新,可以类比为观察者模式。这里是,当一个线程用完资源之后时,则会自动通知依赖它的所有线程。

屏障(barrier)

屏障是另一种线程的同步机制。barrier会建立一个控制点,所有参与的线程会在这里阻塞,直到所有这些参与方都到达这一点。采用这种方法,线程可以单独启动然后暂停,直到所有线程都准备好了才可以继续。

示例如下:

import threading
import time

def worker(barrier):
    print(threading.current_thread().getName(), "worker")
    worker_id = barrier.wait()
    print(threading.current_thread().getName(), worker_id)

threads = []
barrier = threading.Barrier(3)
for i in range(3):
    threads.append(
        threading.Thread(
            name="t" + str(i),
            target=worker,
            args=(barrier,)
        )
    )
for t in threads:
    print(t.name, 'starting')
    t.start()
    time.sleep(0.1)

for t in threads:
    t.join()

运行之后,效果如下:

Python中threading库实现线程锁与释放锁

从控制台的输出会发发现,barrier.wait()会阻塞线程,直到所有线程被创建后,才同时释放越过这个控制点继续执行。wait()的返回值指示了释放的参与线程数,可以用来限制一些线程做清理资源等动作。

当然屏障Barrier还有一个abort()方法,该方法可以使所有等待线程接收一个BroKenBarrierError。如果线程在wait()上被阻塞而停止处理,会产生这个异常,通过except可以完成清理工作。

有限资源的并发访问

除了多线程可能访问同一个资源之外,有时候为了性能,我们也会限制多线程访问同一个资源的数量。例如,线程池支持同时连接,但数据可能是固定的,或者一个网络APP提供的并发下载数支持固定数目。这些连接就可以使用Semaphore来管理。

示例如下:

import threading
import time

class WorkerThread(threading.Thread):
    def __init__(self):
        super(WorkerThread, self).__init__()
        self.lock = threading.Lock()
        self.value = 0

    def increment(self):
        with self.lock:
            self.value += 1
            print(self.value)

def worker(s, pool):
    with s:
        print(threading.current_thread().getName())
        pool.increment()
        time.sleep(1)
        pool.increment()

pool = WorkerThread()
s = threading.Semaphore(2)
for i in range(5):
    t = threading.Thread(
        name="t" + str(i),
        target=worker,
        args=(s, pool,)
    )
    t.start()

运行之后,效果如下:

Python中threading库实现线程锁与释放锁

从图片虽然能看所有输出,但无法看到其停顿的事件。读者自己运行会发现,每次顶多只有两个线程在工作,是因为我们设置了threading.Semaphore(2)。

隐藏资源

在实际的项目中,有些资源需要锁定以便于多个线程使用,而另外一些资源则需要保护,以使它们对并非使这些资源的所有者的线程隐藏。

local()函数会创建一个对象,它能够隐藏值,使其在不同的线程中无法被看到。示例如下:

import threading
import random

def show_data(data):
    try:
        result = data.value
    except AttributeError:
        print(threading.current_thread().getName(), "No value")
    else:
        print(threading.current_thread().getName(), "value=", result)

def worker(data):
    show_data(data)
    data.value = random.randint(1, 100)
    show_data(data)

local_data = threading.local()
show_data(local_data)
local_data.value = 1000
show_data(local_data)

for i in range(2):
    t = threading.Thread(
        name="t" + str(i),
        target=worker,
        args=(local_data,)
    )
    t.start()

运行之后,效果如下:

Python中threading库实现线程锁与释放锁

这里local_data.value对所有线程都不可见,除非在某个线程中设置了这个属性,这个线程才能看到它。

到此这篇关于Python中threading库实现线程锁与释放锁的文章就介绍到这了,更多相关Python 线程锁与释放锁内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Python 相关文章推荐
详解使用python crontab设置linux定时任务
Dec 08 Python
关于Python中异常(Exception)的汇总
Jan 18 Python
python生成随机图形验证码详解
Nov 08 Python
python topN 取最大的N个数或最小的N个数方法
Jun 04 Python
python+opencv+caffe+摄像头做目标检测的实例代码
Aug 03 Python
python用插值法绘制平滑曲线
Feb 19 Python
对python中的six.moves模块的下载函数urlretrieve详解
Dec 19 Python
Python HTML解析模块HTMLParser用法分析【爬虫工具】
Apr 05 Python
Python3实现打印任意宽度的菱形代码
Apr 12 Python
python线程池 ThreadPoolExecutor 的用法示例
Oct 10 Python
Python自动化测试基础必备知识点总结
Feb 07 Python
PyQt5实现多张图片显示并滚动
Jun 11 Python
Python中Cookies导出某站用户数据的方法
May 17 #Python
Python 高级库15 个让新手爱不释手(推荐)
Python带你从浅入深探究Tuple(基础篇)
May 15 #Python
Python中zipfile压缩包模块的使用
python 制作一个gui界面的翻译工具
pyqt5打包成exe可执行文件的方法
Python 机器学习工具包SKlearn的安装与使用
You might like
PHP中实现中文字串截取无乱码的解决方法
2018/05/29 PHP
微信公众平台开发教程⑤ 微信扫码支付模式介绍
2019/04/10 PHP
javascript引用对象的方法
2007/01/11 Javascript
jQuery 开天辟地入门篇一
2009/12/09 Javascript
jQuery UI-Draggable 参数集合
2010/01/10 Javascript
jquery关于图形报表的运用实现代码
2011/01/06 Javascript
jQuery布局插件UI Layout简介及使用方法
2013/04/03 Javascript
javascript实现数字验证码的简单实例
2014/02/10 Javascript
jQuery实现仿百度帖吧头部固定导航效果
2015/08/07 Javascript
HTML5 Shiv完美解决IE(IE6/IE7/IE8)不兼容HTML5标签的方法
2015/11/25 Javascript
React从react-router路由上做登陆验证控制的方法
2018/05/10 Javascript
浅谈react-router@4.0 使用方法和源码分析
2019/06/04 Javascript
javascript中undefined的本质解析
2019/07/31 Javascript
微信js-sdk 录音功能的示例代码
2019/11/01 Javascript
vue实现两个组件之间数据共享和修改操作
2020/11/12 Javascript
原生JavaScript实现进度条
2021/02/19 Javascript
Python上传package到Pypi(代码简单)
2016/02/06 Python
python flask实现分页效果
2017/06/27 Python
Django 连接sql server数据库的方法
2018/06/30 Python
如何在django中添加日志功能
2020/02/06 Python
python使用dlib进行人脸检测和关键点的示例
2020/12/05 Python
matplotlib交互式数据光标mpldatacursor的实现
2021/02/03 Python
CSS3模拟动画下拉菜单效果
2017/04/12 HTML / CSS
Html5 audio标签样式的修改
2016/01/28 HTML / CSS
英国独特的时尚和生活方式品牌:JOY
2018/03/17 全球购物
美国传奇滑手Paul Rodriguez创办的街头滑板品牌:Primitive Skateboarding
2019/10/29 全球购物
main 函数执行以前,还会执行什么代码
2013/04/17 面试题
重写子类方法时,抛出异常的书写注意事项
2015/10/17 面试题
装潢设计实习自我鉴定
2013/09/19 职场文书
社会实践自我鉴定
2013/11/07 职场文书
甜品蛋糕店创业计划书范文
2014/02/06 职场文书
党支部换届选举方案
2014/05/08 职场文书
刑事法律意见书
2015/06/04 职场文书
高中军训感想
2015/08/07 职场文书
《折线统计图》教学反思
2016/02/22 职场文书
CentOS7安装GlusterFS集群以及相关配置
2022/04/12 Servers