Este artículo se centra en cómo obtener más de un bloqueo a la vez si se proporciona un programa multiproceso junto con evitar los puntos muertos.
Programas de subprocesos múltiples: debido a los subprocesos que siguen intentando obtener múltiples bloqueos a la vez, estos son muy propensos a los puntos muertos. Entendiéndolo con un ejemplo: un hilo ya ha adquirido un bloqueo y luego el bloque intenta un segundo bloqueo; en ese caso, el programa puede congelarse ya que el hilo puede bloquear potencialmente el progreso de otros hilos.
Solución:
- Cumplimiento de la regla de pedido
- Asignando cada bloqueo de manera única al programa.
- Solo permite adquirir múltiples bloqueos en orden ascendente.
Código #1: Implementación de la solución usando un administrador de contexto.
Python3
# importing libraries import threading from contextlib import contextmanager # threading to stored information _local = threading.local() @contextmanager def acquire(*lock_state_state): # Object identifier to sort the lock lock_state_state = sorted(lock_state_state, key=lambda a: id(a)) # checking the validity of previous locks acquired = getattr(_local, 'acquired', []) if acquired and max(id(lock_state) for lock_state in acquired) >= id(lock_state_state[0]): raise RuntimeError('lock_state Order Violation') # Collecting all the lock state. acquired.extend(lock_state_state) _local.acquired = acquired try: for lock_state in lock_state_state: lock.acquire() yield finally: # locks are released in reverse order. for lock_state in reversed(lock_state_state): lock_state.release() del acquired[-len(lock_state_state):]
Los bloqueos se adquieren de la manera normal usando el administrador de contexto y para realizar esta tarea se usa la función de adquisición() ya que había más de un bloqueo como se muestra en el código a continuación:
Código #2:
Python3
# threads import threading # creating locks lock_state_1 = threading.Lock() lock_state_2 = threading.Lock() # using acquire as there are more than one lock def thread_1(): while True: with acquire(lock_state_1, lock_state_2): print('Thread-1') def thread_2(): while True: with acquire(lock_state_2, lock_state_1): print('Thread-2') t1 = threading.Thread(target=thread_1) # daemon thread runs without blocking # the main program from exiting t1.daemon = True t1.start() t2 = threading.Thread(target=thread_2) t2.daemon = True t2.start()
- Incluso después de la adquisición de la especificación de bloqueos en un orden diferente en cada función, el programa se ejecutará para siempre sin puntos muertos.
- Ordenar los bloqueos juega un papel importante de acuerdo con el identificador del objeto, ya que los bloqueos después de ser ordenados se adquieren de manera consistente, independientemente de cómo el usuario los haya proporcionado para adquirir().
- Si se anidan varios subprocesos como se muestra en el código a continuación, para resolver un problema sutil con la detección de un punto muerto potencial, se utiliza el almacenamiento local de subprocesos.
Código #3:
Python3
# threads import threading # creating locks lock_state_1 = threading.Lock() lock_state_2 = threading.Lock() def thread_1(): while True: with acquire(lock_state_1): with acquire(lock_state_2): print('Thread-1') def thread_2(): while True: with acquire(lock_state_2): with acquire(lock_state_1): print('Thread-2') t1 = threading.Thread(target=thread_1) # daemon thread runs without blocking # the main program from exiting t1.daemon = True t1.start() t2 = threading.Thread(target=thread_2) t2.daemon = True t2.start()
Al ejecutar esta versión del programa, uno de los subprocesos se bloqueará con una excepción como:
Exception in thread Thread-1: Traceback (most recent call last): File "/usr/HP/lib/python3.3/threading.py", line 639, in _bootstrap_inner self.run() File "/usr/HP/lib/python3.3/threading.py", line 596, in run self._target(*self._args, **self._kwargs) File "deadlock.py", line 49, in thread_1 with acquire(y_lock): File "/usr/HP/lib/python3.3/contextlib.py", line 48, in __enter__ return next(self.gen) File "deadlock.py", line 17, in acquire raise RuntimeError("Lock Order Violation") RuntimeError: Lock Order Violation
Cada subproceso recuerda que el bloqueo ya se adquirió, por eso ha estado mostrando este error. Las restricciones de orden que adquirieron los bloqueos también se aplican y el método de adquisición() comprueba una lista de bloqueos adquiridos previamente.
Publicación traducida automáticamente
Artículo escrito por manikachandna97 y traducido por Barcelona Geeks. The original can be accessed here. Licence: CCBY-SA