Python — многопоточность и истинный цикл while

У меня есть поток, который добавляет строки в self.output и цикл, который выполняется до тех пор, пока self.done не станет истинным (или не будет достигнуто максимальное время выполнения).

Есть ли более эффективный способ сделать это, кроме использования цикла while, который постоянно проверяет, выполнено ли это. Цикл while заставляет процессор загружаться до 100% во время работы.

time.clock()
while True:

    if len(self.output):
        yield self.output.pop(0)

    elif self.done or 15 < time.clock():
        if 15 < time.clock():
            yield "Maximum Execution Time Exceeded %s seconds" % time.clock()
        break

person Ian    schedule 30.04.2009    source источник


Ответы (5)


Ваши потоки добавляются к self.output здесь, и ваша основная задача их потребляет? Если да, то это специальное задание для Queue.Queue. Ваш код должен стать примерно таким:

import Queue

# Initialise queue as:
queue = Queue.Queue()
Finished = object()   # Unique marker the producer will put in the queue when finished

# Consumer:
try:
    while True:
        next_item = self.queue.get(timeout=15)
        if next_item is Finished: break
        yield next_item

except Queue.Empty:
    print "Timeout exceeded"

Ваши потоки производителя добавляют элементы в очередь с помощью queue.put(item)

[Изменить] Исходный код имеет проблему гонки при проверке self.done (например, несколько элементов могут быть добавлены в очередь до установки флага, что приводит к аварийному завершению кода при первом из них) . Обновлено с предложением от ΤΖΩΤΖΙΟΥ — вместо этого поток производителя должен добавить в очередь специальный токен (Finished), чтобы указать, что он завершен.

Примечание. Если у вас есть несколько потоков-производителей, вам потребуется более общий подход к определению завершения их работы. Вы можете добиться этого с помощью той же стратегии: каждый поток имеет маркер Finished, а потребитель завершает работу, когда видит маркеры num_threads.

person Brian    schedule 30.04.2009
comment
Ооооо, теперь мы говорим. :D - person Ian; 01.05.2009
comment
Есть ли способ сообщить блокировке потока в Queue.get() без тайм-аута, что производитель закончил помещать что-либо в поток, чтобы он мог выйти чисто? - person millimoose; 01.05.2009
comment
@Sii: Вы можете пометить демон потока при его создании. Это означает, что поток завершится при выходе из вашей программы. - person John Fouhy; 01.05.2009
comment
Производитель должен поставить в очередь специальный маркер, что все готово. Либо сделайте done_marker= object() и используйте это, либо вы можете использовать объект с многоточием (в противном случае, как правило, бесполезный). - person tzot; 01.05.2009
comment
Я должен отметить, что в вашем примере есть недостаток, if self.done: break следует изменить на: if self.done and self.queue.empty(): break, иначе не обязательно будут включены последние элементы в очереди. мне потребовалось немного поиграть с ним, чтобы понять это. - person Ian; 01.05.2009
comment
Вы правы - маркер «Готово», вероятно, лучший способ избежать проблем с гонкой. Я обновлю код. - person Brian; 01.05.2009

Используйте семафор; пусть рабочий поток освободит его, когда он завершится, и заблокирует добавляемый поток, пока рабочий не закончит работу с семафором.

т.е. в воркере сделайте что-то вроде self.done = threading.Semaphore() в начале работы и self.done.release() по окончании. В коде, который вы отметили выше, вместо цикла занятости просто выполните self.done.acquire(); когда рабочий поток завершится, управление вернется.

Изменить: боюсь, я не обращаюсь к вашему необходимому значению тайм-аута; эта проблема описывает необходимость тайм-аута семафора в стандартной библиотеке.

person esm    schedule 30.04.2009

Используйте time.sleep(seconds) для создания короткой паузы после каждой итерации цикла while, чтобы освободить процессор. Вам нужно будет установить время сна во время каждой итерации в зависимости от того, насколько важно быстро выполнить задание после его завершения.

Пример:

time.clock()
while True:

    if len(self.output):
        yield self.output.pop(0)

    elif self.done or 15 < time.clock():
        if 15 < time.clock():
            yield "Maximum Execution Time Exceeded %s seconds" % time.clock()
            break

    time.sleep(0.01) # sleep for 10 milliseconds
person moinudin    schedule 30.04.2009
comment
сон обычно приводит к плохой работе. вам следует подумать о синхронизации перед использованием сна. - person Francis; 01.05.2009

использовать модуль мьютекса или событие/семафор

person Francis    schedule 30.04.2009

Здесь вы должны использовать примитив синхронизации. Посмотрите здесь: http://docs.python.org/library/threading.html .

Объекты событий кажутся очень простыми и должны решить вашу проблему. Вы также можете использовать объект условия или семафор.

Я не публикую пример, потому что я никогда не использовал объекты Event, а альтернативы, вероятно, менее просты.


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

person Bastien Léonard    schedule 30.04.2009