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之玩转字符串(2)更新篇
Sep 28 Python
python服务器端收发请求的实现代码
Sep 29 Python
Python 数据结构之旋转链表
Feb 25 Python
python实现统计文本中单词出现的频率详解
May 20 Python
详解Python 4.0 预计推出的新功能
Jul 26 Python
Python BeautifulSoup [解决方法] TypeError: list indices must be integers or slices, not str
Aug 07 Python
如何用OpenCV -python3实现视频物体追踪
Dec 04 Python
python如何从键盘获取输入实例
Jun 18 Python
Keras设置以及获取权重的实现
Jun 19 Python
python+selenium 简易地疫情信息自动打卡签到功能的实现代码
Aug 22 Python
python requests库的使用
Jan 06 Python
Python中seaborn库之countplot的数据可视化使用
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加密解密的代码
2007/07/16 PHP
php session_start()出错原因分析及解决方法
2013/10/28 PHP
成为好程序员必须避免的5个坏习惯
2014/07/04 PHP
Yii2创建多界面主题(Theme)的方法
2016/10/08 PHP
php 计算两个时间相差的天数、小时数、分钟数、秒数详解及实例代码
2016/11/09 PHP
Yii框架批量插入数据扩展类的简单实现方法
2017/05/23 PHP
js检测客户端不是firefox则提示下载
2007/04/07 Javascript
JQuery的Validation插件中Remote验证的中文问题
2010/07/26 Javascript
ExtJS4 Grid改变单元格背景颜色及Column render学习
2013/02/06 Javascript
自编jQuery插件实现模拟alert和confirm
2014/09/01 Javascript
jQuery 中$(this).index与$.each的使用指南
2014/11/20 Javascript
javascript 面向对象封装与继承
2014/11/27 Javascript
js实现刷新iframe的方法汇总
2015/04/27 Javascript
用JavaScript实现让浏览器停止载入页面的方法
2017/01/19 Javascript
浅析vue中常见循环遍历指令的使用 v-for
2018/04/18 Javascript
使用Vue组件实现一个简单弹窗效果
2018/04/23 Javascript
Vue.js 中取得后台原生HTML字符串 原样显示问题的解决方法
2018/06/10 Javascript
关于TypeScript模块导入的那些事
2018/06/12 Javascript
微信小程序列表中item左滑删除功能
2018/11/07 Javascript
jQuery实现为table表格动态添加或删除tr功能示例
2019/02/19 jQuery
JS页面获取 session 值,作用域和闭包学习笔记
2019/10/16 Javascript
vue data对象重新赋值无效(未更改)的解决方式
2020/07/24 Javascript
Vue+Bootstrap实现简易学生管理系统
2021/02/09 Vue.js
老生常谈python的私有公有属性(必看篇)
2017/06/09 Python
使用Python制作自动推送微信消息提醒的备忘录功能
2018/09/06 Python
python实现顺时针打印矩阵
2019/03/02 Python
Keras 在fit_generator训练方式中加入图像random_crop操作
2020/07/03 Python
详解CSS3原生支持div铺满浏览器的方法
2018/08/30 HTML / CSS
利用三角函数在canvas上画虚线的方法
2018/01/11 HTML / CSS
意大利领先的奢侈品在线时装零售商:MCLABELS
2020/10/13 全球购物
ktv总经理岗位职责
2014/02/17 职场文书
党支部先进事迹材料
2014/12/24 职场文书
大学生实习证明
2015/06/16 职场文书
浅谈如何写好演讲稿?
2019/06/12 职场文书
2019新学期家长会工作计划
2019/08/21 职场文书
MySQL 数据恢复的多种方法汇总
2021/06/21 MySQL