关于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 ... 
随机推荐
- Django 学习第十二天——Auth 系统
			一.Auth系统中的表: 从表的名称我们就能看出, auth_user,auth_group,auth_permission分别 存放了用户,用户组,权限的信息表. 另外三张表就是多对多的关系表 Us ... 
- python数据结构之希尔排序
			def shell_sort(alist): n=len(alist) gap= int(n / 2) #步长 while gap>0: for i in range(gap,n): j=i w ... 
- 微信小程序开发指南
			https://developers.weixin.qq.com/miniprogram/introduction/#%E4%BA%A7%E5%93%81%E5%AE%9A%E4%BD%8D%E5%8 ... 
- xml模块、项目开发过程
			一.XML模块 xml指的是可扩展标记语言,是一种定义电子文档结构和描述的语言,可以用来标记数据.定义数据类型. 什么时候用xml? 当需要自定义文档结构时,使用xml.在java中经常会使用xml来 ... 
- DRF 权限 频率
			DRF的权限 权限是什么 大家之前都应该听过权限~那么我们权限到底是做什么用的呢~~ 大家都有博客~或者去一些论坛~一定知道管理员这个角色~ 比如我们申请博客的时候~一定要向管理员申请~也就是说管理员 ... 
- ios开发中字符串的常用功能总结
			1.分割字符串 NSString * str1 = @"123/456"; NSArray * arr1 = [str1 componentsSeparatedByString:@ ... 
- 移动应用滑动屏幕方向判断解决方案,JS判断手势方向
			问题分类 滑动屏幕打开相应功能操作. 问题描述 1.用户手动滑动屏幕,根据滑动的方向,打开相应的功能(如:向上滑摇钱树经验明细,向下滑打开任务明细,向左滑打开聚宝盆物品查看等功能),滑动事件捕获问题. ... 
- Hibernate(11)_基于外键的双向1对1
			一.基于外键的双向1对1 对于基于外键的1-1关联,其外键可以存放在任意一边,在需要存放外键一端,增加many-to-one元素.为many-to-one元素增加unique="true&q ... 
- RESTEASY ,从学会使用到了解原理。
			转载本文需注明出处:EAII企业架构创新研究院,违者必究.如需加入微信群参与微课堂.架构设计与讨论直播请直接回复公众号:“EAII企业架构创新研究院”.(微信号:eaworld) 1,背景知识; 1. ... 
- 记录使用yum安装nginx之后的目录问题
			一般来说我们安装nginx的目录都是/usr/local/nginx的,但是用yum安装的目录跟手动安装的不一样,下面是整理的使用yum安装的一些对应目录. nginx执行文件目录:/usr/sbin ... 
