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 相关文章推荐
python3.3教程之模拟百度登陆代码分享
Jan 16 Python
python基础教程之字典操作详解
Mar 25 Python
初步探究Python程序的执行原理
Apr 11 Python
Python创建一个空的dataframe,并循环赋值的方法
Nov 08 Python
PyQt打开保存对话框的方法和使用详解
Feb 27 Python
python+opencv像素的加减和加权操作的实现
Jul 14 Python
Python操作列表常用方法实例小结【创建、遍历、统计、切片等】
Oct 25 Python
python通过移动端访问查看电脑界面
Jan 06 Python
Python 实现打印单词的菱形字符图案
Apr 12 Python
Python多个装饰器的调用顺序实例解析
May 22 Python
Python绘制地图神器folium的新人入门指南
May 23 Python
python游戏开发之pygame实现接球小游戏
Apr 22 Python
Python中Cookies导出某站用户数据的方法
May 17 #Python
Python 高级库15 个让新手爱不释手(推荐)
Python带你从浅入深探究Tuple(基础篇)
May 15 #Python
Python中zipfile压缩包模块的使用
python 制作一个gui界面的翻译工具
pyqt5打包成exe可执行文件的方法
Python 机器学习工具包SKlearn的安装与使用
You might like
smarty 缓存控制前的页面静态化原理
2013/03/15 PHP
PHP中常用的字符串格式化函数总结
2014/11/19 PHP
PHP数据分析引擎计算余弦相似度算法示例
2017/08/08 PHP
PHP学习笔记之session
2018/05/06 PHP
使用indexOf等在JavaScript的数组中进行元素查找和替换
2013/09/18 Javascript
JavaScript中的对象序列化介绍
2014/12/30 Javascript
js使用DOM操作实现简单留言板的方法
2015/04/10 Javascript
jQuery获取URL请求参数的方法
2015/07/18 Javascript
JavaScript代码实现禁止右键、禁选择、禁粘贴、禁shift、禁ctrl、禁alt
2015/11/17 Javascript
jquery实现简单的遮罩层
2016/01/08 Javascript
jQuery自定义元素右键点击事件(实现案例)
2017/04/28 jQuery
详解Angular2中Input和Output用法及示例
2017/05/21 Javascript
JavaScript之DOM插入更新删除_动力节点Java学院整理
2017/07/03 Javascript
javascript中神奇的 Date对象小结
2017/10/12 Javascript
nodejs通过钉钉群机器人推送消息的实现代码
2019/05/05 NodeJs
JavaScript代理模式原理与用法实例详解
2020/03/10 Javascript
Taro UI框架开发小程序实现左滑喜欢右滑不喜欢效果的示例代码
2020/05/18 Javascript
python实现的登录和操作开心网脚本分享
2014/07/09 Python
使用Python 统计高频字数的方法
2019/01/31 Python
Python网络爬虫之爬取微博热搜
2019/04/18 Python
python itchat实现调用微信接口的第三方模块方法
2019/06/11 Python
让Python脚本暂停执行的几种方法(小结)
2019/07/11 Python
python实现电子书翻页小程序
2019/07/23 Python
Python 实现遥感影像波段组合的示例代码
2019/08/04 Python
python实现对列表中的元素进行倒序打印
2019/11/23 Python
python3中关于excel追加写入格式被覆盖问题(实例代码)
2020/01/10 Python
美国一家著名的儿童鞋制造商:Stride Rite
2017/01/02 全球购物
Farfetch中文官网:奢侈品牌时尚购物平台
2020/03/15 全球购物
销售职业生涯规划范文
2014/03/14 职场文书
房屋授权委托书范本
2014/10/07 职场文书
刘公岛导游词
2015/02/05 职场文书
2015年外联部工作总结
2015/04/03 职场文书
驻村工作简报
2015/07/20 职场文书
2015国庆节放假通知范文
2015/07/30 职场文书
《初涉尘世》读后感3篇
2020/01/10 职场文书
Windows Server 修改远程桌面端口的实现
2022/06/25 Servers