最近工作中多个项目都开始用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 使用问题的更多相关文章

  1. Python协程中使用上下文

    在Python 3.7中,asyncio 协程加入了对上下文的支持.使用上下文就可以在一些场景下隐式地传递变量,比如数据库连接session等,而不需要在所有方法调用显示地传递这些变量.使用得当的话, ...

  2. python 协程(单线程中的异步调用)(转廖雪峰老师python教程)

    协程,又称微线程,纤程.英文名Coroutine. 协程的概念很早就提出来了,但直到最近几年才在某些语言(如Lua)中得到广泛应用. 子程序,或者称为函数,在所有语言中都是层级调用,比如A调用B,B在 ...

  3. python 并发专题(十三):asyncio (二) 协程中的多任务

    . 本文目录# 协程中的并发 协程中的嵌套 协程中的状态 gather与wait . 协程中的并发# 协程的并发,和线程一样.举个例子来说,就好像 一个人同时吃三个馒头,咬了第一个馒头一口,就得等这口 ...

  4. Python 协程总结

    Python 协程总结 理解 协程,又称为微线程,看上去像是子程序,但是它和子程序又不太一样,它在执行的过程中,可以在中断当前的子程序后去执行别的子程序,再返回来执行之前的子程序,但是它的相关信息还是 ...

  5. 带你简单了解python协程和异步

    带你简单了解python的协程和异步 前言 对于学习异步的出发点,是写爬虫.从简单爬虫到学会了使用多线程爬虫之后,在翻看别人的博客文章时偶尔会看到异步这一说法.而对于异步的了解实在困扰了我好久好久,看 ...

  6. day-5 python协程与I/O编程深入浅出

    基于python编程语言环境,重新学习了一遍操作系统IO编程基本知识,同时也学习了什么是协程,通过实际编程,了解进程+协程的优势. 一.python协程编程实现 1.  什么是协程(以下内容来自维基百 ...

  7. 终结python协程----从yield到actor模型的实现

    把应用程序的代码分为多个代码块,正常情况代码自上而下顺序执行.如果代码块A运行过程中,能够切换执行代码块B,又能够从代码块B再切换回去继续执行代码块A,这就实现了协程 我们知道线程的调度(线程上下文切 ...

  8. Python 协程 61

    什么是协程 协程,又称微线程,纤程.英文名Coroutine.一句话说明什么是线程:协程是一种用户态的轻量级线程. 协程的特点 协程拥有自己的寄存器上下文和栈.协程调度切换时,将寄存器上下文和栈保存到 ...

  9. 从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 ...

随机推荐

  1. Oracle no TOP, how to get top from order

    On ROWNUM and Limiting Results Our technologist explains how ROWNUM works and how to make it work fo ...

  2. html 知识点

    web服务本质 import socket def main(): sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.bind ...

  3. Redis自学笔记:3.4入门-列表类型

    3.4列表类型 3.4.1介绍 列表类型可以存储一个有序的字符串列表,常用的操作是向列表两端添加元素,或者 获得列表的某一片段. 优点:内部使用的是双向链表,所以向列表两端添加元素的时间复杂度为O(1 ...

  4. BZOJ.4558.[JLOI2016]方(计数 容斥)

    BZOJ 洛谷 图基本来自这儿. 看到这种计数问题考虑容斥.\(Ans=\) 没有限制的正方形个数 - 以\(i\)为顶点的正方形个数 + 以\(i,j\)为顶点的正方形个数 - 以\(i,j,k\) ...

  5. BZOJ.5093.[Lydsy1711月赛]图的价值(NTT 斯特林数)

    题目链接 对于单独一个点,我们枚举它的度数(有多少条边)来计算它的贡献:\[\sum_{i=0}^{n-1}i^kC_{n-1}^i2^{\frac{(n-2)(n-1)}{2}}\] 每个点是一样的 ...

  6. NOIP复习篇

    NOIP复习篇---枚举 --------------------------------------------------------------------------------------- ...

  7. HTML5:在移动端禁用长按选中文本功能

    很多时候,我们在写的手机页面需要用户进行长按然后响应一个事件.但是在微信中用户的长按操作被默认为谈出来一个复制的选项.那么这个时候如何去禁止这个东西呢? 其实很简单,方法看下面: 只需要在你需要禁止的 ...

  8. CallContext,ThreadStatic,AsyncLocal<T>,ThreadLocal<T>,学习笔记

    1.CallContext 在当前调用上下文的线程数据槽里存储对象 2.ThreadStatic 是一个特性 3.AsyncLocal<T> 是一个类型,该字段应当为static,保证单例 ...

  9. Ruby语法基础(一)

    Ruby语法基础(一) Ruby是一种开源的面向对象程序设计的服务器端脚本语言,最初由松本行弘(Matz)设计开发,追求『快乐和生产力』,程序员友好型,被称为『human-oriented langu ...

  10. JS_高程5.引用类型(1)Object类型

    引用类型 在ECMASCript中,引用类型是一种数据结构,将数据和功能组织在一起,引用类型有时候也被称为对象定义,因为它们描述的是一类对象所具有的属性和方法.(注意:尽管ECMAScript从技术上 ...