专栏首页coder修行路关于python协程中aiorwlock 使用问题

关于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

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Python Every Class Needs a __repr__

    当我们在Python中定义一个类的时候,如果我们通过print打印这个类的实例化对象,或者我们直接输入这个类实例化对象会返回怎么样的结果,如下代码:

    coders
  • 关于python中pika模块的问题

    工作中经常用到rabbitmq,而用的语言主要是python,所以也就经常会用到python中的pika模块,但是这个模块的使用,也给我带了很多问题,这里整理一...

    coders
  • 关于java中死锁的总结

    关于死锁,估计很多程序员都碰到过,并且有时候这种情况出现之后的问题也不是非常好排查,下面整理的就是自己对死锁的认识,以及通过一个简单的例子来来接死锁的发生,自己...

    coders
  • 520这天,我突然意识到,她根本配不上我这么聪明的男人!

    在这个重大节日---520情人节来临之际,我却是显得更加寂寞无聊。看着那张长图有点不爽(关键是朋友圈狗粮吃得有点多),于是.........就有了下面这张动态图...

    小小詹同学
  • 一日一技:在Python类里面初始化自己

    这里的 __init__叫做 构造函数。它负责在类初始化为实例的时候,初始化必要的数据。如下图所示:

    青南
  • 实战 | 用Python放一场浪漫的烟花秀!

    https://jizhi.im/blog/post/py_make_fireworks

    昱良
  • Simplex 单纯形算法的python

    算法可以在给定一个包含线性规划问题的标准形式的描述下,求解该线性规划问题。 例如某一个 pro.txt 文件内容如下:

    py3study
  • python post传输文件脚本

    ps:背景,无法ssh相互访问机器(一般中间有堡垒机阻拦)的情况下,但是使用域名可以进行访问的情况下 可以使用http协议进行文件的上传。

    py3study
  • 小甲鱼《零基础学习Python》课后笔记(三十七):类和对象——面向对象编程

    1.当程序员不想把同一段代码写几次,他们发明了函数解决了这种情况。当程序员已经有了一个类,而又想建立一个非常接近的新类,他们会怎么做呢? 定义一个新类继承已有...

    小火柴棒
  • 面向对象封装、继承、多态

    身份运算符用于 比较 两个对象的 内存地址 是否一致 —— 是否是对同一个对象的引用

    py3study

扫码关注云+社区

领取腾讯云代金券