Основные потоки в 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 5

3 ответа:

Здесь у вас есть две проблемы синхронизации.

Давайте сначала разберемся с более простым, с тем фактом, что у вас общий глобальный buff_list, за который борются два потока. Ничто не мешает одному потоку пытаться append одновременно с другим потоком pops, что является незаконным. И даже если Вам повезет и этого не случится, 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()).

Лучший способ я думаю, что научиться продевать нити вглубь-это начать с нуля.