es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

¿Cómo puedo mejorar el mecanismo de bloqueo de asyncio?

A continuación se muestra mi código en el que he implementado el mecanismo de bloqueo asyncio que debe bloquear una solicitud en el método si la solicitud con el mismo nombre ya está en ejecución y aún no se ha completado, lo cual funciona correctamente, pero el problema es que si la solicitud viene con un nombre diferente también se bloquea, lo cual no es ideal, idealmente debería ser que si la solicitud viene con un nombre de solicitud diferente debería comenzar su ejecución sin tener que esperar.

import asyncio
from contextlib import asynccontextmanager

@asynccontextmanager
async def get_lock(req_name_):
    locks = {}
    logger.info(f"Creando bloqueo para la pila {req_name_} si no está creado")
    if not locks.get(req_name_):
        logger.info("creando clave para un bloqueo")
        locks[req_name_] = asyncio.Lock()
    async with locks[req_name_]:
        yield
    if len(locks[req_name_]._waiters) == 0:
        del locks[req_name_]
        logger.info("bloqueo liberado")
    logger.info(len(locks))

async def handle_lock_request(req_json_):
    logger.info("ocupando bloqueo")
    req_name = req_json_.get('req_name')
    async with get_lock(req_name):
        logger.info(f"bloqueo adquirido por la pila {req_name}")
        await _handle_request(req_json_)

async def _req_handler():
    tasks = []
    loop = asyncio.get_running_loop()
    logger.debug("Await receiver.recv_string")
    req = await receiver.recv_string()
    logger.debug(f"Solicitud recibida {req}")
    req_json = json.loads(req)
    logger.debug("Await create_task")
    tasks.append(loop.create_task(handle_lock_request(req_json)))
    await asyncio.gather(*[task for task in tasks if not task.done()])

def _handle_request(req_json_):
    # ...
    # ...
    logger.info(f"Solicitud finalizada con nombre de solicitud {req_name} para la acción patch stack")
Tags:  , , ,

Answer

  1. Avatar for davy.ai

    El problema con el código es que la variable locks está definida dentro de la función get_lock y es una variable local. Esto significa que cada vez que se llama a get_lock, se crea un nuevo diccionario locks.

    Para solucionar el problema, locks debería ser definido fuera de la función get_lock para que sea una variable compartida en todas las llamadas a get_lock.

    Aquí tienes una versión actualizada del código:

    import asyncio
    from contextlib import asynccontextmanager
    
    locks = {}
    
    @asynccontextmanager
    async def get_lock(req_name_):
        logger.info(f"Creando bloqueo para la pila {req_name_} si no está creado")
        if not locks.get(req_name_):
            logger.info("Creando clave para un bloqueo")
            locks[req_name_] = asyncio.Lock()
        async with locks[req_name_]:
            yield
        if len(locks[req_name_]._waiters) == 0:
            del locks[req_name_]
            logger.info("Bloqueo liberado")
        logger.info(len(locks))
    
    async def handle_lock_request(req_json_):
        logger.info("Ocupando bloqueo")
        req_name = req_json_.get('req_name')
        async with get_lock(req_name):
            logger.info(f"Bloqueo adquirido por la pila {req_name}")
            await _handle_request(req_json_)
    
    async def _req_handler():
        tasks = []
        loop = asyncio.get_running_loop()
        logger.debug("Esperando receiver.recv_string")
        req = await receiver.recv_string()
        logger.debug(f"Request recibida {req}")
        req_json = json.loads(req)
        logger.debug("Creando tarea")
        tasks.append(loop.create_task(handle_lock_request(req_json)))
        await asyncio.gather(*[task for task in tasks if not task.done()])
    
    def _handle_request(req_json_):
        # ...
        # ...
        logger.info(f"Solicitud finalizada con nombre de solicitud {req_name} para la acción de parche de la pila")
    

    Con este cambio, el diccionario locks ahora es compartido en todas las llamadas a get_lock, lo que significa que las solicitudes con diferentes nombres no serán bloqueadas por el bloqueo de una solicitud diferente.

Comments are closed.