Основные потоки в python
Хорошо, код довольно простой. Поскольку я использую несколько потоков и хочу, чтобы между ними были общие переменные, я использую глобальный.
Почему код в ThreadClass иногда не выполняется, когда я нажимаю "C"? Я знаю, что это проблема параллелизма, но я не знаю, как это исправить. В последнее время я читал о семафорах и блокировках, но я не совсем уверен, как это реализовать на данный момент. Любые предложения приветствуются.
import threading
buff_list = []
class ThreadClass(threading.Thread):
global buff_list
def run(self):
while (True):
if ("C" == raw_input()):
buff_list.append("C")
print buff_list
class ThreadClass2(threading.Thread):
global buff_list
def run(self):
while(True):
if ("B" == raw_input() and len(buff_list) > 0):
buff_list.pop()
print buff_list
a = ThreadClass()
b = ThreadClass2()
a.start()
b.start()
3 ответа:
Здесь у вас есть две проблемы синхронизации.
Давайте сначала разберемся с более простым, с тем фактом, что у вас общий глобальныйbuff_list
, за который борются два потока. Ничто не мешает одному потоку пытатьсяappend
одновременно с другим потокомpop
s, что является незаконным. И даже если Вам повезет и этого не случится,pop
может прийти раньшеappend
.
Самый простой способ решить эту проблему-использовать
Queue
, что именно автоматическая синхронизация:Тогда просто используйтеbuff_list = Queue.Queue()
put
вместоappend
иget
вместоpop
.
Однако, если вы хотите научиться этому самостоятельно,есть два возможных пути.Во-первых, вы можете использовать
Lock
. (Вы также можете использоватьRLock
, но давайте пока забудем об этом.) Это гарантирует, что только один поток получает доступ кbuff_list
одновременно.buff_lock = threading.Lock() buff_list = []
Теперь, всякий раз, когда вы добавляете или хлопаете, просто хватайте замок:
with buff_lock: buff_list.append("C") with buff_lock: val = buff_list.pop()
Но это не гарантирует, что выскакивающий код будет ждать, пока что-то не выскочит. Если вы хотите сделать это, используйте
Condition
:buff_cond = threading.Condition()
Теперь:
with buff_cond: buff_list.append("C") buff_cond.notify() with buff_cond: while not buff_list: buff_cond.wait() value = buff_list.pop()
Вторая проблема заключается в том, что вы неявно разделяетеsys.stdin
, потому что оба потока вызываютraw_input
. Если только у вас нет способа синхронизировать вещи так, чтобы каждый поток знал, когда он должен получить следующий вход (и это может быть трудно даже описать, и вы не можете превратить его в код, если вы не можете описать его), это не может работать-каждый раз, когда вы набираетеC
, есть 50/50 шансов, что неправильный поток получит его. Таким образом, как предлагает кирелагин, вам нужно сделать ровно один поток ответственным за ввод/вывод.самый простой способ сделать это-снова использоватьQueue
, и иметь один потокput
любые входные данные, которые он не использует, а другой поток можетget
из очереди.
Ну, вы никогда не знаете, экземпляр какого класса получил ваш ввод. Если вы нажмете " C " и это будет
ThreadClass2
чтение вашего ввода, он просто ничего не сделает, так как"B" == raw_input()
будетFalse
.Ровно один поток должен отвечать за ввод-вывод
В дополнение к тому, что было сказано в предыдущих ответах, я хотел бы добавить, что модуль threading также поддерживает более примитивные механизмы, реализованные также во всех других языках, таких как Semaphore Objects.
это один из старейших примитивов синхронизации в истории информатики, изобретенный ранним голландским ученым-компьютерщиком Эдсгером У. Дейкстрой (он использовал P() и V() вместо acquire() и release()).
Лучший способ я думаю, что научиться продевать нити вглубь-это начать с нуля.