SQLAlchemy独家资源分配

2024-04-25 00:12:54 发布

您现在位置:Python中文网/ 问答频道 /正文

我正在编写一个模块来维护某种独占资源(在本例中是电影票)。模型类就像

class User(Base):
    __tablename__ = 'user'

    id = Column('id', Integer, primary_key=True)
    name = Column('name', String)

class Ticket(Base):
    __tablename__ = 'ticket'

    id = Column('id', Integer, primary_key=True)
    user_id = Column('user_id', ForeignKey('user.id'))
    user = relationship(User)
    seat = Column('seat', String)

可能会有购买门票的并发请求,但当然每个请求只能卖给一个用户。所以我写了一些这样的代码

def acquire_multiple(session, user_id, ticket_id_list):
    session.begin()
    try:
        for tid in ticket_id_list:
            ticket = session.query(Ticket).filter(Ticket.id == tid, Ticket.user_id == None).first()
            if ticket is None:
                raise RuntimeError('ticket sold')
            ticket.user_id = user_id
            session.add(ticket)
        session.commit()
    except:
        session.rollback()
        raise

不知道这是可行的,还是更好的计划?你知道吗


Tags: keynameidtruebasesessioncolumninteger
1条回答
网友
1楼 · 发布于 2024-04-25 00:12:54

我并不想粗鲁,但我只想分享一些核心的编码原则,这些原则是绝对基本的,需要在代码中加以说明。你知道吗

请永远不要引发运行时错误。他们是有原因的。 另外,如果要捕获异常,应该捕获实际的异常,而不要写入“except:”,否则可能会捕获到不打算处理的异常,并可能得到意外的结果。另外,请不要捕捉您在try块中抛出的异常。 您的:

if ticket is None:
    raise RuntimeError('ticket sold')
...
except:
    session.rollback()
...

实际上可以重写为:

if not ticket:
    session.rollback()
    raise ValueError('ticket sold')

这是pythonic,是正确的写作方式。实际上,您可以将整个for循环重写为:

if ticket not in ticket_id_list:
    raise ...

因此,您不必在python中迭代它(与C中的内部for循环相反)。你也应该采取

ticket = session.query(Ticket).filter(Ticket.id == tid, Ticket.user_id == None).first()

在for循环之外,并将它放在它的正上方,这样如果保留for循环,就不会进行额外的函数调用和添加到堆栈中。你知道吗

现在讨论实际问题:您需要在从螺纹。锁紧()或线程.RLock(). 列表的某些方面可以是线程安全的,但最好不要假设。另外,通过它的for循环不是。你需要一个锁来确保票不会被出示两次,而且只给一个顾客。老实说,从体系结构的角度来看,我会让这个方法在票证可用时返回票证,或者在票证不可用时返回None,然后一级一级地处理它,或者一级一级地处理它,然后将它返回到视图中,让它在得到NoType时处理“票证已售出”消息。。 但你可以这样做:

lock = threading.RLock()

def acquire_multiple(session, user_id, ticket_id_list):
    session.begin()
    with lock:
        try:
            ticket = session.query(Ticket).filter(Ticket.id == tid, Ticket.user_id == None).first()
            try:
                ticket_id_list.remove(ticket)
            except ValueError:
                print("ticket sold")
                session.add(None)
            else:
                session.add(ticket)
        except AttributeError:
            print("No ticket has been selected this session!")
            session.add(None)
        finally:
            session.commit()

当你退出功能时,你会松开锁,然后其他人可以试着取票,票就已经卖了。你知道吗

两次试一试是绝对必要的。第一种是防止在你的会话中没有票,第二种是被认为是“pythonic”,或者“请求原谅,而不是允许”,所以我们假设它在列表中,并处理它不在列表中的可能性。在某些情况下,检查它是否在列表中更好(一个循环的性能更快),这不是这些情况之一。你知道吗

相关问题 更多 >