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中用memcached来减少数据库查询次数的教程
Apr 07 Python
实例讲解Python中函数的调用与定义
Mar 14 Python
Python构建XML树结构的方法示例
Jun 30 Python
python利用urllib和urllib2访问http的GET/POST详解
Sep 27 Python
详解python实现识别手写MNIST数字集的程序
Aug 03 Python
在Python中将函数作为另一个函数的参数传入并调用的方法
Jan 22 Python
Python循环结构的应用场景详解
Jul 11 Python
python 字符串常用函数详解
Sep 11 Python
Python爬取新型冠状病毒“谣言”新闻进行数据分析
Feb 16 Python
Python生成六万个随机,唯一的8位数字和数字组成的随机字符串实例
Mar 03 Python
Python内建序列通用操作6种实现方法
Mar 26 Python
python用TensorFlow做图像识别的实现
Apr 21 Python
Python中Cookies导出某站用户数据的方法
May 17 #Python
Python 高级库15 个让新手爱不释手(推荐)
Python带你从浅入深探究Tuple(基础篇)
May 15 #Python
Python中zipfile压缩包模块的使用
python 制作一个gui界面的翻译工具
pyqt5打包成exe可执行文件的方法
Python 机器学习工具包SKlearn的安装与使用
You might like
PHP4实际应用经验篇(7)
2006/10/09 PHP
推荐个功能齐全的发送PHP邮件类
2007/01/03 PHP
PHP使用SWOOLE扩展实现定时同步 MySQL 数据
2017/04/09 PHP
Laravel第三方包报class not found的解决方法
2019/10/13 PHP
Laravel定时任务的每秒执行代码
2019/10/22 PHP
数组方法解决JS字符串连接性能问题有争议
2011/01/12 Javascript
JS中confirm,alert,prompt函数区别分析
2011/01/17 Javascript
用JS实现一个TreeMenu效果分享
2011/08/28 Javascript
javascript完美拖拽的实现方法
2013/09/29 Javascript
使用js解决由border属性引起的div宽度问题
2013/11/26 Javascript
Jquery的Tabs内容轮换效果实现代码,几行搞定
2014/02/12 Javascript
动态创建script标签实现跨域资源访问的方法介绍
2014/02/28 Javascript
Javascript中的几种继承方式对比分析
2016/03/22 Javascript
jQuery实现导航高亮的方法【附demo源码下载】
2016/11/09 Javascript
使用bootstrap插件实现模态框效果
2017/05/10 Javascript
vue 1.x 交互实现仿百度下拉列表示例
2017/10/21 Javascript
Vue常用指令详解分析
2018/08/19 Javascript
JS+HTML5 canvas绘制验证码示例
2018/12/05 Javascript
微信小程序textarea层级过高(盖住其他元素)问题的解决办法
2019/03/04 Javascript
详解vue配置后台接口方式
2019/03/29 Javascript
微信小程序实现判断是分享到群还是个人功能示例
2019/05/03 Javascript
JS中的算法与数据结构之集合(Set)实例详解
2019/08/20 Javascript
Vue 中 template 有且只能一个 root的原因解析(源码分析)
2020/04/11 Javascript
JavaScript本地储存:localStorage、sessionStorage、cookie的使用
2020/10/13 Javascript
python创建只读属性对象的方法(ReadOnlyObject)
2013/02/10 Python
对TensorFlow的assign赋值用法详解
2018/07/30 Python
详解python tkinter教程-事件绑定
2019/03/28 Python
python selenium爬取斗鱼所有直播房间信息过程详解
2019/08/09 Python
python自动生成model文件过程详解
2019/11/02 Python
Python3读写Excel文件(使用xlrd,xlsxwriter,openpyxl3种方式读写实例与优劣)
2020/02/13 Python
会计电算化应届生自荐信
2014/02/25 职场文书
党支部季度考核意见
2015/06/02 职场文书
2019年作为一名实习生的述职报告
2019/09/29 职场文书
Python网络编程之ZeroMQ知识总结
2021/04/25 Python
详解Vue的options
2021/05/15 Vue.js
如何理解PHP核心特性命名空间
2021/05/28 PHP