aprendizagem de impasse em python

Exemplos

Este artigo resume os artigos de impasse do Python e explica parte do conteúdo incompreensível.
Primeiro enumere uma situação em que ocorrerá um conflito

死锁的一个原因是互斥锁。假设银行系统中,用户a试图转账100块给用户b,
与此同时用户b试图转账200块给用户a,则可能产生死锁。
2个线程互相等待对方的锁,互相占用着资源不释放。
#coding=utf-8
import time
import threading
class Account:
  def __init__(self, _id, balance, lock):
    self.id = _id
    self.balance = balance
    self.lock = lock

  def withdraw(self, amount):
    self.balance -= amount

  def deposit(self, amount):
    self.balance += amount


def transfer(_from, to, amount):
  if _from.lock.acquire():#锁住自己的账户
    _from.withdraw(amount)
    time.sleep(1)#让交易时间变长,2个交易线程时间上重叠,有足够时间来产生死锁
    print('wait for lock...')
    if to.lock.acquire():#锁住对方的账户
      to.deposit(amount)
      to.lock.release()
    _from.lock.release()
  print( 'finish...')

a = Account('a',1000, threading.Lock())
b = Account('b',1000, threading.Lock())
threading.Thread(target = transfer, args = (a, b, 100)).start()
threading.Thread(target = transfer, args = (b, a, 200)).start()

Nos programas multithread, grande parte do problema de deadlock é causada pelo encadeamento de vários bloqueios ao mesmo tempo. Por exemplo: um encadeamento adquire o primeiro bloqueio e, em seguida, bloqueia ao adquirir o segundo bloqueio, esse encadeamento pode bloquear a execução de outros encadeamentos, resultando na morte de todo o programa. Uma solução para o problema do impasse é atribuir um ID exclusivo para cada bloqueio no programa e permitir apenas que vários bloqueios sejam usados ​​em ordem crescente.Esta regra é muito fácil de implementar usando um gerenciador de contexto

Primeiro, veja o que é o gerenciador de contexto @contextmanager.
A função decorada pelo decorador é dividida em três partes:

  1. O bloco de código na instrução with executa o código antes do rendimento na função
  2. O conteúdo retornado pelo rendimento é copiado para a variável depois de
  3. Depois que o bloco com código é executado, o código após o rendimento na função é executado

Solução

import threading
from contextlib import contextmanager

# Thread-local state to stored information on locks already acquired
_local = threading.local()


@contextmanager
def acquire(*locks):
  # Sort locks by object identifier
  locks = sorted(locks, key=lambda x: id(x))

  # Make sure lock order of previously acquired locks is not violated
  acquired = getattr(_local, 'acquired', [])
  #如果出现新锁id大于旧锁id 报错
  if acquired and max(id(lock) for lock in acquired) >= id(locks[0]):
    raise RuntimeError('Lock Order Violation')
  # Acquire all of the locks
  acquired.extend(locks)
  _local.acquired = acquired
  try:
    for lock in locks:
      lock.acquire()
    yield		#无返回值 yield为空
  finally:
    # Release locks in reverse order of acquisition
    for lock in reversed(locks):
      lock.release()
    del _local.acquired[-len(locks):]

Obviamente, existem certos problemas na aquisição de bloqueios em ordem crescente, consulte o código a seguir

import threading
x_lock = threading.Lock()
y_lock = threading.Lock()
 
def thread_1():
  while True:
        with acquire(x_lock):
            print("acquire x_lock")
            time.sleep(1)
            with acquire(y_lock):
                print('Thread-1')
                time.sleep(1)


def thread_2():
  while True:
        with acquire(y_lock):
            print('acquire y_lock')
            time.sleep(1)
            with acquire(x_lock):
                print('Thread-2')
 
t1 = threading.Thread(target=thread_1)
t1.daemon = False
t1.start()
 
t2 = threading.Thread(target=thread_2)
t2.daemon = False
t2.start()

daemon:
(1)如果某个子线程的daemon属性为False,主线程结束时会检测该子线程是否结束,
如果该子线程还在运行,则主线程会等待它完成后再退出;
(2)如果某个子线程的daemon属性为True,主线程运行结束时不对这个子线程进行检
查而直接退出,同时所有daemon值为True的子线程将随主线程一起结束,而不论
是否运行完成。

O resultado da execução do código é uma exceção.O
Insira a descrição da imagem aqui
motivo da falha é que cada thread registra o bloqueio que adquiriu. A função adquirir () verifica a lista de bloqueios adquiridos anteriormente.Como os bloqueios são adquiridos em ordem crescente, a função assume que o ID do bloqueio adquirido anteriormente deve ser menor que o bloqueio recém-adquirido e uma exceção é acionada.
O motivo do resultado acima é que a exceção apareceu pela primeira vez no Thread-2, causando a interrupção do thread. Depois disso, não haverá conflito, porque apenas um encadeamento Thread-1 está em execução e, através do gerenciador de contexto, o bloqueio será liberado automaticamente após cada chamada de aquisição interna, portanto, não haverá conflito.

Resumo:

Evitar deadlock é uma maneira de resolver o problema de deadlock.Quando o processo adquire o bloqueio, ele é organizado em ordem crescente estrita do ID do objeto.Depois de provas matemáticas, isso garante que o programa não entre no estado de deadlock. A idéia principal de evitar deadlock é que simplesmente adicionar bloqueios na ordem crescente de ID do objeto não criará uma dependência circular, e a dependência circular é uma condição necessária para o deadlock, para evitar que o programa entre em um estado de deadlock.

Publicado 16 artigos originais · Gosto1 · Visitas 375

Acho que você gosta

Origin blog.csdn.net/qq_41174940/article/details/104620663
Recomendado
Clasificación