如何在并发运行的函数中一次执行部分代码?

2022-01-15 00:00:00 python discord.py sql sqlite

所以,让我简要介绍一下我想要实现的目标.我有一个名为 apply 的命令,只要有人输入该命令,apply 函数就会开始执行.现在,apply 函数有一些我想同时执行的代码,以防许多用户同时触发该命令,并且有一个名为 register 的异步函数我想执行一次一次.这是代码的一瞥:

So, let me give you a brief of what am trying to achieve. I've a command called apply, as soon as some one enters that command, execution of apply function begins. Now, apply function have some part of code which I want to be executed concurrently in case if many users triggered that command at the same time, and there is a async function called register which I want to be executed once at a time. Here's the glimpse of the code:

@client.command()
async def apply(ctx):
 #Stuff I want to be run concurrently
 await register(ctx.author)

async def register(user):
 # I've an another python file 'db.py' for accessing the database 
 data = await db.execute_query('SELECT Total_Reg from Data WHERE rowid = 1')
 total = data[0][0] # Getting the total registrations value and storing it in total variable     
 if total >= max_reg: # If total reg. are more or equal to max value then close the reg.
  print("Registrations are closed!")
  return
 # Otherwise if registrations are open then increase the value of total reg by 1
 # And register the user with unique registration number [reg_no] allotted to him
 await db.execute_query('UPDATE Data SET Total_Reg = Total_Reg + 1 WHERE rowid = 1')
 reg_no = total + 1
 registrations[reg_no] = user
 await close_registration() 
 """Also, in case of Queue or execution of this function once at a time, I 
    want this function to continue executing the code and shouldn't wait for 
    this line of code particularly, i.e await close_registration() should 
    keep on executing in background without blocking the current execution of 
    this function. So should I use asyncio.ensure_future(close_registration()) 
    instead of await or what?"""

我希望你明白我想在这里做什么.现在,让我们来看看我的问题.apply 函数在有人输入 apply 命令时被不和谐触发,因此可能存在 data = await db.execute_query('SELECT Total_Reg fromData WHERE rowid = 1') 可以同时执行多次,这将导致返回相同数量的注册,并存储在所有这些变量的总变量中.

I hope you get the idea what am trying to do here. Now, let's come to my query. apply function is triggered by discord as soon as some one enters apply command, so there can be some case that data = await db.execute_query('SELECT Total_Reg from Data WHERE rowid = 1') can be executed multiple times at the same time which will cause the same number of registrations at that time to be returned and stored in total variable of all of them.

我知道,当已经有连接更新或访问数据库时,sqlite 会锁定数据库,但是一旦执行查询,另一个查询将在 await db.execute_query('UPDATEData SET Total_Reg = Total_Reg + 1 WHERE rowid = 1') 执行增量查询.

I know, sqlite locks the database when there is already a connection updating or accessing the database but then also, as soon as a query is executed, another will start executing there itself before await db.execute_query('UPDATE Data SET Total_Reg = Total_Reg + 1 WHERE rowid = 1') Increment query is executed.

我想要的基本上是,

这应该是顺序:

  1. 使用 获取当前注册数 data = await db.execute_query('SELECT Total_Reg from Data WHERE rowid = 1')
  2. 检查注册是否已满
  3. 如果没有,则注册用户并通过增加当前编号来更新数据库.await db.execute_query('UPDATE Data SET Total_Reg = Total_Reg + 1 WHERE rowid = 1')
  4. 的注册次数为 1
  1. Getting the current number of registrations with data = await db.execute_query('SELECT Total_Reg from Data WHERE rowid = 1')
  2. Checking if registration is full or not
  3. If not, then register the user and update the database by increasing the current no. of registrations by 1 with await db.execute_query('UPDATE Data SET Total_Reg = Total_Reg + 1 WHERE rowid = 1')

例如,如果有 2 个用户同时申请注册,那么应该先注册一个,然后再注册另一个,并且他们的 reg_no 应该是唯一的.

For example, if 2 users applied to register at the same time, then one should be registered first and then only another should get registered and reg_no for both of them should be unique.

所以,就是这样.我怎样才能实现这个功能?我应该使用队列吗?或者sqlite中是否有任何功能可以首先获取当前的reg.然后如果它小于 max_reg 则将值增加 1 并且所有这些都在一个查询中?

So, that's it. How can I achieve this functionality? Should I use a Queue? Or is there any feature in sqlite to first get the current reg. and then increase the value by 1 if it's less than max_reg and all this in just a single query?

谢谢!

推荐答案

可能存在 SQL 解决方案,但您可以在 discord.py 的帮助下继续此操作

Well there possibly exists an SQL solution, but you could proceed with this with the help of discord.py

@commands.max_concurrency(1, commands.BucketType.guild, True)
@commands.command() or #@bot.command() depends on what you are using
async def apply(ctx):
    #command here

@commands.max_concurrency(number, bucket_type, wait) 让您控制命令的并发.
number 是可以同时使用的最大次数.
bucket_type 是应该检查并发性的地方,公会意味着在单个公会中,如果您希望它是全局使用 commands.BucketType.default.
wait 是如果有人使用该命令而不应该运行它应该做的事情.如果我们将其设置为 true,它将等待命令完成运行,如果 wait 为 false,它将引发一个错误,该错误可以被错误处理程序捕获.

@commands.max_concurrency(number, bucket_type, wait) lets you control the concurrency of the command.
number is the max number of times it can be used concurrently.
bucket_type is where the concurrency is supposed to be checked, guild means in a single guild, if you want it to be global use commands.BucketType.default.
wait is what it should do if someone uses the command and its not supposed to run. if we set it to true it will wait till the command finishes running, if wait is false it will raise an error which can be caught with an error handler.

如果你只想让一个函数同时执行一次,

If you just want a function to be executed once concurrently,

@commands.command(hidden=True)
@commands.max_concurrency(1, commands.BucketType.guild, True)
async def register(ctx, args):
   #do stuff

但是人们可以使用注册.

However people will be able to use register.

@commands.command()
async def apply(args):
   ctx.command = bot.get_command('register')
   await bot.invoke(ctx)

非常混乱的代码,但应该可以工作.

Very messy code, but should work.

  • max_concurrency
  • BucketType

相关文章