Уровень новичка проблемы многопоточности Python

Попробуйте заменить эти строки:

random_index = random.randrange(len(enemies))

x = random.randint(0, 5)
7
задан 5 revs, 3 users 51% 27 January 2009 в 20:53
поделиться

5 ответов

Поточная обработка с PyGTK является битом, хитрым, если Вы хотите сделать его правильно. В основном Вы не должны обновлять GUI ни из какого другого потока, чем основной поток (общее ограничение в GUI освобождает). Обычно это сделано в PyGTK с помощью механизма сообщений с очередями (для коммуникации между рабочими и GUI), которые периодически читаются с помощью функции тайм-аута. После того как у меня была презентация моего локального LUG по этой теме, можно захватить пример кода для этой презентации от Репозитория кода Google. Взгляните на MainWindow класс в forms/frmmain.py, особенно для метода _pulse() и в чем выполняют on_entry_activate() (поток запускается там плюс неактивный таймер, создается).

def on_entry_activate(self, entry):
    text = entry.get_text().strip()
    if text:
        store = entry.get_completion().get_model()
        if text not in [row[0] for row in store]:
            store.append((text, ))
        thread = threads.RecommendationsFetcher(text, self.queue)# <- 1
        self.idle_timer = gobject.idle_add(self._pulse)# <- 2
        tv_results = self.widgets.get_widget('tv_results')
        model = tv_results.get_model()
        model.clear()
        thread.setDaemon(True)# <- 3
        progress_update = self.widgets.get_widget('progress_update')
        progress_update.show()
        thread.start()# <- 4

Таким образом, обновления приложения GUI, когда "неактивно" (средствами GTK) порождение никаких замораживаний.

  • 1: создайте поток
  • 2: создайте неактивный таймер
  • 3: обсуждение daemonize так приложение может закрыться, не ожидая завершения потока
  • 4: запустите поток
9
ответ дан 6 December 2019 в 19:43
поделиться

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

Важная вещь понять состоит в том, что приложение GUI делает многое ни из чего. Это проводит большую часть своего времени, ожидая ОС, чтобы сказать этому, что что-то произошло. Можно сделать много материала в это время простоя, пока Вы знаете, как написать продолжительный код, таким образом, он не блокируется.

Можно решить исходную проблему при помощи тайм-аута; сообщение платформы GUI отозвать некоторую функцию после задержки, и затем сброс той задержки или запуск другого отложенного вызова.

Другой общий вопрос состоит в том, как связаться по сети в приложении GUI. Сетевые приложения похожи на приложения для GUI в этом, они делают большое ожидание. Используя платформу сети IO (как Скрученный) помогает иметь обе части Вашего приложения, ожидают совместно вместо соревновательно, и снова облегчает потребность в дополнительных потоках.

Продолжительные вычисления могут быть записаны многократно вместо синхронно, и можно сделать обработку, в то время как GUI неактивен. Можно использовать генератор, чтобы сделать это довольно легко в Python.

def long_calculation(param, callback):
    result = None
    while True:
        result = calculate_next_part(param, result)
        if calculation_is_done(result):
            break
        else:
            yield
    callback(result)

Вызов long_calculation даст Вам объект генератора и вызов .next() на объекте генератора выполнит генератор, пока он не достигнет также yield или return. Вы просто сказали бы платформе GUI звонить long_calculation(some_param, some_callback).next когда это будет иметь время, и в конечном счете Ваш обратный вызов назовут с результатом.

Я не знаю GTK очень хорошо, таким образом, я не могу сказать Вам, какой gobject функционирует, необходимо звонить. С этим объяснением, тем не менее, необходимо смочь найти необходимые функции в документации, или в худшем случае, спросить относительно соответствующего канала IRC.

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

3
ответ дан 6 December 2019 в 19:43
поделиться

Вы не можете перезапустить остановленный объект потока; не пробовать. Вместо этого создайте новый экземпляр объекта, если Вы хотите перезапустить его после того, как его действительно останавливают и присоединяются.

1
ответ дан 6 December 2019 в 19:43
поделиться

Я не посмотрел подробно на Вашем коде. Но я вижу два решения Вашей проблемы:

Не используйте потоки вообще. Вместо этого используйте тайм-аут, как это:

import gobject

i = 0
def do_print():
    global i
    print i
    i += 1
    if i == 10:
        main_loop.quit()
        return False
    return True

main_loop = gobject.MainLoop()
gobject.timeout_add(250, do_print)
main_loop.run()

При использовании потоков необходимо удостовериться, что код GUI только называют от одного потока одновременно путем защиты его как это:

import threading
import time

import gobject
import gtk

gtk.gdk.threads_init()

def run_thread():
    for i in xrange(10):
        time.sleep(0.25)
        gtk.gdk.threads_enter()
        # update the view here
        gtk.gdk.threads_leave()
    gtk.gdk.threads_enter()
    main_loop.quit()
    gtk.gdk.threads_leave()

t = threading.Thread(target=run_thread)
t.start()
main_loop = gobject.MainLoop()
main_loop.run()
0
ответ дан 6 December 2019 в 19:43
поделиться

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

make_idle является функциональным декоратором, который позволяет Вам выполнять задачу в фоновом режиме совместно. Это - хороший компромисс между чем-то достаточно коротким, чтобы работать однажды в потоке UI и не влиять на скорость отклика приложения, и выполнение полного распараллеливают в специальной синхронизации. В украшенной функции Вы используете "урожай" для возвращения обработки GUI, таким образом, это может остаться быстро реагирующим и в следующий раз, когда UI неактивен, это возьмет в функции, где Вы кончили. Таким образом для запущения этого Вы просто называете idle_add к украшенной функции.

def make_idler(func):
    """
    Decorator that makes a generator-function into a function that will
continue execution on next call
    """
    a = []

    @functools.wraps(func)
    def decorated_func(*args, **kwds):
        if not a:
            a.append(func(*args, **kwds))
        try:
            a[0].next()
            return True
        except StopIteration:
            del a[:]
            return False

    return decorated_func

Если необходимо сделать немного больше обработки, можно использовать менеджера по контексту для блокировки потока UI при необходимости, чтобы помочь сделать код немного более безопасным

@contextlib.contextmanager
def gtk_critical_section():
    gtk.gdk.threads_enter()
    try:
        yield
    finally:
        gtk.gdk.threads_leave()

с этим Вы можете просто

with gtk_critical_section():
    ... processing ...

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

0
ответ дан 6 December 2019 в 19:43
поделиться
Другие вопросы по тегам:

Похожие вопросы: