Вы хотите что-то по следующим направлениям.
parent.py
import subprocess
c1= subprocess.Popen(["python", "child.py", "1"], stdin=subprocess.PIPE, stdout=subprocess.PIPE)
c2= subprocess.Popen(["python", "child.py", "2"], stdin=subprocess.PIPE, stdout=subprocess.PIPE)
out1, err1= c1.communicate("to 1: hit it!")
print " 1:", repr(out1)
print "*1:", repr(err1)
out2, err2= c2.communicate("to 2: ready, set, go!")
print " 2:", repr(out2)
print "*2:", repr(err2)
out1, err1= c1.communicate()
print " 1:", repr(out1)
print "*1:", repr(err1)
out2, err2= c2.communicate()
print " 2:", repr(out2)
print "*2:", repr(err2)
c1.wait()
c2.wait()
child.py
import yourDBconnection as dbapi2
def child1():
print "Child 1 start"
conn= dbapi2.connect(...)
c1= conn.cursor()
conn.begin() # turn off autocommit, start a transaction
ra= c1.execute("UPDATE A SET AC1='Achgd' WHERE AC1='AC1-1'")
print ra
print "Child1", raw_input()
rb= c1.execute("UPDATE B SET BC1='Bchgd' WHERE BC1='BC1-1'")
print rb
c1.close()
print "Child 1 finished"
def child2():
print "Child 2 start"
conn= dbapi2.connect(...)
c1= conn.cursor()
conn.begin() # turn off autocommit, start a transaction
rb= c1.execute("UPDATE B SET BC1='Bchgd' WHERE BC1='BC1-1'")
print rb
print "Child2", raw_input()
ra= c1.execute("UPDATE A SET AC1='Achgd' WHERE AC1='AC1-1'")
print ta
c1.close()
print "Child 2 finish"
try:
if sys.argv[1] == "1":
child1()
else:
child2()
except Exception, e:
print repr(e)
Обратите внимание на симметрию. Каждый ребенок запускает один ресурс. Затем они пытаются получить чужой ресурс. Вы можете, для удовольствия, иметь 3 детей и 3 ресурсов для действительно порочного круга.
Обратите внимание, что затруднение в создании ситуации, при которой происходит тупик. Если ваши транзакции короткие и последовательные - тупик очень трудно достичь. Для взаимоблокировки требуется (а) транзакция, которая долго удерживает блокировки И (б) транзакции, которые приобретают блокировки в непоследовательном порядке. Я нашел, что проще всего предотвратить блокировки, сохраняя мои транзакции короткими и последовательными.
Также обратите внимание на недетерминизм. Вы не можете предсказать, какой ребенок умрет с тупиком, и который будет продолжаться после смерти другого. Только один из них должен умереть, чтобы освободить необходимые ресурсы для другого. Некоторые утверждения РСУБД о том, что существует правило, основанное на количестве ресурсов, принадлежащих бла-бла-бла, но в целом вы никогда не узнаете, как был выбран жертва.
Из-за того, что две записи находятся в определенном порядке, вы предпочитаете, чтобы ребенок 1 умирал первым. Однако вы не можете этого гарантировать. Это не тупик, пока ребенок 2 не попытается получить ресурсы ребенка 1 - последовательность того, кто приобрел первый, не может определить, кто умирает.
Также обратите внимание, что это процессы, а не потоки. Темы - из-за Python GIL - могут быть непреднамеренно синхронизированы и потребуют много вызовов до time.sleep(0.001)
, чтобы дать другому потоку шанс наверстать упущенное. Процессы - для этого - немного проще, потому что они полностью независимы.
Это просто вызовет OperationalError: (1205, «Тайм-аут блокировки ожидания превышен, попробуйте перезапустить транзакцию»), нет? – Greg 2008-11-06 22:10:52
@Greg: Я думаю, что они говорят о том, что один сеанс делает LOCK TABLE A, а другой сеанс делает LOCK TABLE B. В этом случае нужно как-то синхронизировать. Затем сеанс один пытается БЛОКИРОВАТЬ ТАБЛИЦУ B. Когда сеанс второй попытки LOCK TABLE A - он закроется. – 2008-11-07 10:56:33