2015-09-25 2 views
1

Код я написал ниже, предполагается выводить что-то вдоль линий этого:Неправильного форматирования при использовании многопоточности и очередей

<Floor(Thread-3, started 44660)> <Bank(Thread-1, started 43356)> shutting down 
<Floor(Thread-4, started 44108)> received a message: shutting down (0, 'UP') 
<Bank(Thread-1, started 43356)> received a message: (1, 'DOWN') 
<Bank(Thread-1, started 43356)> shutting down 
<Bank(Thread-2, started 27800)> shutting down 

Однако форматирование вывода кажется непоследовательным в разы. Например:

<Floor(Thread-3, started 27076)> <Bank(Thread-1, started 44608)>shutting down 
<Floor(Thread-4, started 28772)>received a message: (shutting down0, 'UP') 

<Bank(Thread-1, started 44608)> received a message: (1, 'DOWN') 
<Bank(Thread-1, started 44608)> shutting down 
<Bank(Thread-2, started 41480)> shutting down 

Соответствующие данные важны для большинства программ. Почему этот результат несовместим и как его предотвратить?

import threading 
import Queue 

banks = [] 
floors = [] 

class Bank(threading.Thread): 
    def __init__(self): 
     threading.Thread.__init__(self) 
     self.mailbox = Queue.Queue() 
     banks.append(self.mailbox) 

    def run(self): 
     while True: 
      data = self.mailbox.get() 
      if data == 'shutdown': 
       print self, 'shutting down' 
       return 
      print self, 'received a message:', data 

    def stop(self): 
     banks.remove(self.mailbox) 
     self.mailbox.put('shutdown') 
     self.join() 

class Floor(threading.Thread): 
    def __init__(self, number = 0): 
     threading.Thread.__init__(self) 
     self.mailbox = Queue.Queue() 
     floors.append(self.mailbox) 
     self.number = number 

    def run(self): 
     while True: 
      data = self.mailbox.get() 
      if data == 'shutdown': 
       print self, 'shutting down' 
       return 
      print self, 'received a message:', data 

    def stop(self): 
     floors.remove(self.mailbox) 
     self.mailbox.put('shutdown') 
     self.join() 

    def call(self, data): 
     banks[0].put((self.number, data)) 

b0 = Bank() 
b1 = Bank() 
b0.start() 
b1.start() 
f0 = Floor(0) 
f1 = Floor(1) 
f0.start() 
f1.start() 
f0.call('UP') 
f1.call('DOWN') 
f0.stop() 
f1.stop() 
b0.stop() 
b1.stop() 
+0

Под «непоследовательным» вы действительно имели в виду только дополнительную строку новой строки или был ли вывод более чередующимся? – Andy

+0

@ Andy Пробелы и символы новой строки являются расходными (но мне интересно, почему они также непоследовательны). Самая интригующая часть - '(shutting down0, 'UP')'. Каким-то образом эти сообщения время от времени конкатенируются. – Mast

ответ

2

В конечном счете, так как ваши call() звонки теперь выполняются в различных нитей, нет ничего, что гарантирует, что они будут выполнять (и выход) синхронно. Если вы хотите гарантировать, что вывод не чередуется, вам нужно разместить большую глобальную блокировку (mutex) вокруг ваших вызовов печати.

Даже это не гарантирует, что они будут упорядочены последовательно - например, различные строки, приведенные выше, можно легко переключить - в этом случае вам нужно будет заблокировать цепочку вызовов. Кстати, это будет отрицать почти любое преимущество в производительности многопоточности в первую очередь.

+0

Я предположил, что stdout будет востребован одним и после того, как он будет завершен другим. Порядок исполнения не имеет значения, но чередование проблематично. Значит, у меня все получилось? – Mast

+0

'print' не является потокобезопасным ... Но даже если бы это было так, это только разрешило бы проблему чередования между строками. Линии сами по-прежнему могут переключаться. Так что да, если вы хотите синхронный выход, вам нужен синхронный дизайн - потоки - это асинхронные звери. – Andy

1

print не является потокобезопасным. Однако, sys.stdout.write есть.

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

Смежные вопросы