关于python协程中aiorwlock 使用问题
最近工作中多个项目都开始用asyncio aiohttp aiomysql aioredis ,其实也是更好的用python的协程,但是使用的过程中也是遇到了很多问题,最近遇到的就是
关于aiorwlock 的问题,在使用中碰到了当多个协程同时来请求锁的时候 在其中一个还没释放锁的时候,另外一个协程也获取到锁,这里进行整理,也希望知道问题你解决方法的,一起讨论一下,正好最近经常用到协程的东西,所以准备建一个群,也欢迎大家一起进来讨论python协程的内容,群号:692953542
关于场景的描述

数据库的要操作的表的信息为:
| id | name | nickname | count | flag | crdate |
| 1 | 800100 | aa | 100 | 1 | 2018-11-18 10:07:22 |
| 2 | 800101 | bb | 200 | 1 | 2018-11-18 10:07:23 |
当多个请求都到数据库操作接口程序的时候,针对同一个name的count进行增加或者减少,就要保证操作的同一个时刻只有一个可以去获取count的值并进行update操作,所以我是在这一步增加了锁,因为使用aiohttp写的,所以想要在这里也用了aiorwlock,但是在我测试的过程中发现了,当一个协程获取锁还没释放锁的时候,另外一个协程也获取到锁,下面我是具体的代码
程序代码
核心的处理类:
class CntHandler(object):
def __init__(self, db, loop):
self.db = db
self.loop = loop
self.company_lock = {}
def response(self, request, msg):
peer = request.transport.get_extra_info('peername')
logging.info("request url[%s] from[%s]: %s", request.raw_path, peer, msg)
origin = request.headers.get("Origin")
if origin is not None:
headers = {"Access-Control-Allow-Origin": origin, "Access-Control-Allow-Credentials": "true"}
resp = web.Response(text=util.dictToJson(msg), content_type='application/json', headers=headers)
else:
resp = web.Response(text=util.dictToJson(msg), content_type='application/json')
return resp
async def cnt_set(self, request):
"""
用于设置company表中的count值
:param request:
:return:
"""
post = await request.post()
logging.info('post %s', post)
company_name = post.get("company")
cnt = post.get("cnt")
sql = "update shield.company set count=%s where name=%s"
args_values = [cnt, company_name]
rwlock = self.company_lock.get(company_name, "")
if not rwlock:
rwlock = aiorwlock.RWLock(loop=self.loop)
self.company_lock[company_name] = rwlock
async with rwlock.writer:
msg = dict()
po_sql = "select * from shield.company where name=%s"
po = await self.db.get(po_sql, company_name)
if not po: # 找不到企业
logging.error("not found company name [%s]", company_name)
msg["code"] = 404
msg["code"] = "not found company"
return self.response(request, msg)
res = await self.db.execute(sql, args_values)
if not isinstance(res, int):
logging.error("sql update is err:", res)
msg["code"] = 403
msg["reason"] = "set fail"
return self.response(request, msg)
logging.info("company [%s] set cnt [%s] is success", company_name, cnt)
msg["code"] = 200
msg["reason"] = "ok"
return self.response(request, msg)
async def cnt_inc(self, request):
"""
用于增加company表中的count值
:param request:
:return:
"""
post = await request.post()
logging.info('post %s', post)
company_name = post.get("company")
cnt = int(post.get("cnt", 0))
rwlock = self.company_lock.get(company_name, "")
if not rwlock:
rwlock = aiorwlock.RWLock(loop=self.loop)
self.company_lock[company_name] = rwlock
async with rwlock.writer:
uuid_s = uuid.uuid1().hex
logging.debug("[%s]---[%s]", uuid_s, id(rwlock))
msg = dict()
sql = "select * from shield.company where name=%s"
po = await self.db.get(sql, company_name)
if not po: # 找不到企业
logging.error("not found company name [%s]", company_name)
msg["code"] = 404
msg["code"] = "not found company"
return self.response(request, msg)
old_cnt = po.get("count")
po_cnt = int(po.get("count"))
res = po_cnt + cnt
update_sql = "update shield.company set count=%s where name=%s"
args_values = [res, company_name]
update_res = await self.db.execute(update_sql, args_values)
if not isinstance(update_res, int): # 数据库update失败
logging.error("sql update is err:", update_res)
msg["code"] = 403
msg["reason"] = "inc fail"
return self.response(request, msg)
logging.info("uuid [%s] lock [%s] company [%s] inc cnt [%s] old cnt [%s] true will is [%s] success", uuid_s,id(rwlock), company_name, cnt, old_cnt, res)
msg["code"] = 200
msg["reason"] = "ok"
return self.response(request, msg)
async def cnt_dec(self, request):
"""
用于减少company表中count的值
:param request:
:return:
"""
post = await request.post()
logging.info('post %s', post)
company_name = post.get("company")
cnt = int(post.get("cnt", 0))
rwlock = self.company_lock.get(company_name, "")
if not rwlock:
rwlock = aiorwlock.RWLock(loop=self.loop)
self.company_lock[company_name] = rwlock
async with rwlock.writer:
uuid_s = uuid.uuid1().hex
logging.debug("[%s]---[%s]", uuid_s, id(rwlock))
msg = dict()
sql = "select * from shield.company where name=%s"
po = await self.db.get(sql, company_name)
if not po: # 找不到企业
logging.error("not found company name [%s]", company_name)
msg["code"] = 404
msg["code"] = "not found company"
return self.response(request, msg)
po_cnt = int(po.get("count"))
old_cnt = po.get("count")
if po_cnt == 0:
logging.error("company [%s] cnt is 0", company_name)
msg["code"] = 400
msg["reason"] = "cnt is 0"
return self.response(request, msg)
if po_cnt < cnt: # 数据库余额不足
logging.error("company [%s] count is not enough", company_name)
msg["code"] = 405
msg["reason"] = "count is not enough"
return self.response(request, msg)
res = po_cnt - cnt
update_sql = "update shield.company set count=%s where name=%s"
args_values = [res, company_name]
update_res = await self.db.execute(update_sql, args_values)
if not isinstance(update_res, int): # 执行update 失败
logging.error("sql update is err:", update_res)
msg["code"] = 403
msg["reason"] = "inc fail"
return self.response(request, msg)
logging.info("uuid [%s] lock [%s] company [%s] dec cnt [%s] old cnt [%s] true will is [%s] success",uuid_s,id(rwlock), company_name, cnt, old_cnt, res)
msg["code"] = 200
msg["reason"] = "ok"
return self.response(request, msg)
上面代码出问题的代码是在增加和减少的时候:
async with rwlock.writer:
在一个协程还没有释放锁的时候,另外一个操作也就进来了,到之后我在测试并发的时候,对同一个name的count进行操作导致最后的count值不符合的问题
可能是我本身代码的问题,或者我哪里处理的不对,欢迎大家一起讨论
这个完整的代码地址:https://github.com/pythonsite/test_aiorwlock
关于python协程中aiorwlock 使用问题的更多相关文章
- Python协程中使用上下文
在Python 3.7中,asyncio 协程加入了对上下文的支持.使用上下文就可以在一些场景下隐式地传递变量,比如数据库连接session等,而不需要在所有方法调用显示地传递这些变量.使用得当的话, ...
- python 协程(单线程中的异步调用)(转廖雪峰老师python教程)
协程,又称微线程,纤程.英文名Coroutine. 协程的概念很早就提出来了,但直到最近几年才在某些语言(如Lua)中得到广泛应用. 子程序,或者称为函数,在所有语言中都是层级调用,比如A调用B,B在 ...
- python 并发专题(十三):asyncio (二) 协程中的多任务
. 本文目录# 协程中的并发 协程中的嵌套 协程中的状态 gather与wait . 协程中的并发# 协程的并发,和线程一样.举个例子来说,就好像 一个人同时吃三个馒头,咬了第一个馒头一口,就得等这口 ...
- Python 协程总结
Python 协程总结 理解 协程,又称为微线程,看上去像是子程序,但是它和子程序又不太一样,它在执行的过程中,可以在中断当前的子程序后去执行别的子程序,再返回来执行之前的子程序,但是它的相关信息还是 ...
- 带你简单了解python协程和异步
带你简单了解python的协程和异步 前言 对于学习异步的出发点,是写爬虫.从简单爬虫到学会了使用多线程爬虫之后,在翻看别人的博客文章时偶尔会看到异步这一说法.而对于异步的了解实在困扰了我好久好久,看 ...
- day-5 python协程与I/O编程深入浅出
基于python编程语言环境,重新学习了一遍操作系统IO编程基本知识,同时也学习了什么是协程,通过实际编程,了解进程+协程的优势. 一.python协程编程实现 1. 什么是协程(以下内容来自维基百 ...
- 终结python协程----从yield到actor模型的实现
把应用程序的代码分为多个代码块,正常情况代码自上而下顺序执行.如果代码块A运行过程中,能够切换执行代码块B,又能够从代码块B再切换回去继续执行代码块A,这就实现了协程 我们知道线程的调度(线程上下文切 ...
- Python 协程 61
什么是协程 协程,又称微线程,纤程.英文名Coroutine.一句话说明什么是线程:协程是一种用户态的轻量级线程. 协程的特点 协程拥有自己的寄存器上下文和栈.协程调度切换时,将寄存器上下文和栈保存到 ...
- 从yield 到yield from再到python协程
yield 关键字 def fib(): a, b = 0, 1 while 1: yield b a, b = b, a+b yield 是在:PEP 255 -- Simple Generator ...
随机推荐
- Jmeter元件——JSON Extractor后置处理器
场景使用 json extractor后置处理器用在返回格式为json的HTTP请求中,用来获取返回的json中的某个值.并保存成变量供后面的请求进行调用或断言等. 使用方法 1.常规操作 路径:选择 ...
- vim编辑
vim 重点在于光标的移动,模式的切换,删除,查找,替换,复制,黏贴,撤销命令的使用 vim的三种模式:命令模式(打开文件默认进入此模式)编辑模式(输入模式)末行模式(按:键进入,只能从命令模式下按键 ...
- Jenkins部署码云SpringBoot项目到远程服务器
本文是上一篇文章的后续,上一篇只是利用Jenkins部署项目到本地,并启动,本文是将项目部署到远程服务器并执行. 1.环境准备 1.1 安装插件 上一篇文章已经介绍了需要安装的应用及插件,这一篇还需要 ...
- SpringBoot整合dubbo
Dubbo是阿里巴巴公司开源的一个高性能优秀的服务框架,使得应用可通过高性能的 RPC 实现服务的输出和输入功能,可以和Spring框架无缝集成. 以上介绍来源于百度百科,具体dubbo相关可以自行查 ...
- 洛谷P1774 最接近神的人_NOI导刊2010提高(02)(求逆序对)
To 洛谷.1774 最接近神的人 题目描述 破解了符文之语,小FF开启了通往地下的道路.当他走到最底层时,发现正前方有一扇巨石门,门上雕刻着一幅古代人进行某种活动的图案.而石门上方用古代文写着“神的 ...
- curl获取结果乱码的解决方法之CURLOPT_ENCODING(curl/Post请求)
//php脚本开始 /*POST请求远程内容函数*/ function ppost($url,$data,$ref){ // 模拟提交数据函数 $curl = curl_init( ...
- C语言基础一(敲打键盘、寻找资料)
事前声明一点:小编的所有材料都是基础,没有什么大的不同,您若觉得不错的话,可以互相探讨下,毕竟本人也是小雏鸟. 大家在学习C语言.C++类似的高端语言时候,往往都是为了学而学,殊不知为什么而学,或许更 ...
- jmeter接口测试实例4-学生金币充值
Jmeter实例4:学生金币充值 添加http协议—添加IP.路径.方法,添加cookie管理器,察看结果树如下图所示 输入管理员名称:niuhanyang,输入值,域,如下图所示: 输入必填参数,运 ...
- 深入理解this,bind、call
直接看this 直接看call和bind 首先放一道题: var a={ a:'haha', getA: function(){ console.log(this.a); } } var b= { a ...
- GitHub上传本地文件
基本条件:安装GitHub,安装成功之后:(windows系统) 1.安装完成后,还需要一步设置,在命令行输入: $ git config --global user.name "Your ...