2012-06-18 7 views
1

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

Задание: В качестве части моей магистерской диссертации мне нужно создать игру с смешанной реальностью, которая включает в себя многопользовательскую способность. в моем игровом дизайне каждый игрок может установить множество ловушек, каждый из которых активен в течение определенного периода времени, например. 30 сек. Чтобы поддерживать согласованное состояние игры для всех игроков, необходимо проверить все время на стороне сервера, которая реализована на Python.

Я решил начать нить python, каждый раз, когда новая ловушка укладывается игроком и запускает таймер на потоке. Вся эта часть прекрасна, но реальная проблема возникает, когда мне нужно уведомить главный поток о том, что время для этой конкретной ловушки, так что я могу сообщить об этом клиенту (устройство Android).

Я попытался создать очередь и вставить информацию в очередь, когда задача выполнена, но я не могу сделать queue.join(), так как она будет помещать основной поток на удержание, пока задача не будет выполнена, и это не то, что я не нуждаюсь и не идеален в моем случае, поскольку основной поток постоянно общается с клиентом, и если он остановлен, тогда вся связь с игроками остановится.

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

Любые другие предложения о том, как эта задача может быть достигнута без запуска Gazillion потоков, также приветствуется .. :) :)

Заранее спасибо за помощь ..

Приветствия

ответ

2

Я наконец нашел красивый планировщик задач, написанный на python, который на самом деле довольно легкий и весьма удобный для планирования событий на более поздний срок или дату с помощью механизма обратного вызова, который позволяет дочернему потоку передавать значение к основному потоку, уведомляющему о главном потоке его статуса и о том, успешно ли выполнено задание или нет.

людей там, которые нуждаются в такой же функциональности, как и в вопросе и не хотят торговаться вокруг с потоками можно использовать этот планировщик планировать свои события и получить обратный вызов, когда событие выполняется

здесь ссылка на APScheduler

1

Может быть, проще, чтобы таймеры выполнялись в основном потоке - есть список таймеров, к которым вы добавляете новые. Каждый таймер фактически не do ничего, у него просто есть время, когда он уходит - что легче, если вы работаете в произвольных «раундах», чем в реальном времени, но все же выполнимо. Каждый интервал, mainloop должен проверить все из них и посмотреть, не истечет ли время (или прошлое время) для их истечения - если это так, удалите их из списка (конечно, будьте осторожны при удалении элементов из списка, повторное повторение - возможно, оно не будет делать то, что вы ожидаете).

Если у вас есть много таймеров, и профилирования вы обнаружите, что проходит через все из них каждый интервал обходится вам слишком много времени, простая оптимизация будет держать их в heapq - это будет держать они отсортированы для вас, поэтому вы знаете после первого, который еще не истек, но ни один из остальных не имеет. Что-то вроде:

while True: 
    if not q: 
     break 
    timer = heapq.heappop(q) 
    if timer.expiry <= currenttime: 
     # trigger events 
    else: 
     heapq.heappush(q) 
     break 

Это делает еще стоить вам одну ненужную поп/нажимной пару, но его трудно понять, как вы могли бы сделать лучше - опять же, делать что-то вроде:

for timer in q: 
    if timer.expiry <= currenttime: 
     heapq.heappop(timer) 
     # trigger events 
    else: 
     break 

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

Важно только то, что currenttime постоянно обновляется каждый интервал в основном цикле (или, если ваше сердце на иметь его в режиме реального времени, на основе системных часов), и timer.expiry измеряется в тех же единицах - если у вас есть концепция «раундов», а ловушка длится шесть раундов, когда она будет размещена, вы сделаете heapq.heappush(q, Timer(expiry=currenttime+6).

Если вы хотите сделать это многопоточным способом, ваш способ иметь очередь производителей/потребителей для очистки будет работать - вам просто не нужно использовать Queue.join(). Вместо этого, когда таймер в потоке заканчивается, он вызывает q.put(), а затем умирает. Mainloop будет использовать q.get(False), что позволит избежать блокировки, иначе q.get(True, 0.1), который будет блокировать не более 0,1 секунды - тайм-аут может быть любым положительным числом; тщательно настройте его для лучшего компромисса между блокировкой достаточно долго, чтобы клиенты замечали, и события уходят поздно, потому что они просто пропустили время во время очереди.

+0

спасибо за быстрый ответ .. я обязательно посмотрю идею кучи и посмотрю, могу ли я использовать ее в своем сценарии. У меня есть один вопрос, хотя об идее многопоточного способа: – vivek86

+0

, когда я должен точно выполнить q.get() ?? поскольку все потоки работают одновременно, мне нужно написать бесконечный цикл в основном потоке, постоянно проверять все потоки и быть живыми или мертвыми, а затем получать информацию от них? То, что я хотел или скорее имел в виду, было чем-то вроде уведомления основной темы. Напр. предположим, что существует 10 потоков, отличных от основного потока, все начаты в разное время и с разной продолжительностью времени. и предположим, что таймер thread_4 просто закончился, он должен уведомить главный поток о том, что время его работы. – vivek86

+1

@ vivek86 очередь представляет собой очередь производителей-потребителей, разделяемую между всеми потоками. Рабочие потоки являются производителями - они вызывают 'q.put (timer_info)', когда заканчивается их таймер. То есть очередь - это ваш механизм уведомления. Основной поток будет иметь очень похожий цикл в однопоточном случае, который я описал, - он берет все с очереди по очереди и общается с ней. Не нужно проверять каждый поток или даже знать, сколько потоков выполняется - как только поток отправил свое уведомление через очередь, ему больше нечего делать и может выйти (если вы не используете их для повышения эффективности). – lvc

0

Основной поток создает очередь и кучу рабочих потоков, которые вытягивают задачи из очереди. Пока очередь пуста, все рабочие блокируют потоки и ничего не делают. Когда задача ставится в очередь, случайный рабочий поток получает задание, выполняет ли он работу и спит, как только ее готов. Таким образом, вы можете повторно использовать поток снова и снова без , создавая новые рабочие потоки.

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

+0

спасибо .. посмотрим на это ..:) :) – vivek86

+0

Проблема с использованием фиксированного числа рабочих потоков (и, действительно, проблема с потоками вообще для этого) заключается в том, что OP, скорее всего, заботится о таймерах, начиная с нужного времени. Чтение рабочих строк из рабочей очереди имеет наибольший смысл, когда вам не все равно, когда начинаются вещи, просто они начинаются, когда есть рабочий. – lvc

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