Выполнить функцию Python в основном потоке из вызова в фиктивном потоке

У меня есть сценарий Python, который обрабатывает асинхронные обратные вызовы из .NET Remoting. Эти обратные вызовы выполняются в фиктивном (рабочем) потоке. Изнутри обработчика обратного вызова мне нужно вызвать функцию, которую я определил в своем скрипте, но мне нужно, чтобы функция выполнялась в основном потоке.

Основной поток - это удаленный клиент, который отправляет команды серверу. Некоторые из этих команд приводят к асинхронным обратным вызовам.

По сути, мне нужен эквивалент метода .NET Invoke. Это возможно?


person Jim C    schedule 24.09.2013    source источник
comment
что делает основной поток? вам нужно как-то сигнализировать ему и заставить его вызвать функцию. если основной поток обычно ожидает, что что-то произойдет, то сигнализируйте об этом. если основной поток выполняет обработку, то поместите функцию, которая будет вызвана, в очередь, и пусть основной поток будет проверять и периодически обрабатывать очередь.   -  person Claudiu    schedule 24.09.2013
comment
Хммм ... подумать подробнее .... если обратный вызов происходит, когда Main ожидает, пока сервер вернет управление из вызова, нет никакого способа заставить Main делать что-нибудь еще, не так ли?   -  person Jim C    schedule 24.09.2013
comment
Хм, нет, вам придется заставить Main ждать одного из двух: либо сервер, чтобы вернуть управление, либо запланированный вызов функции. Вы думали об использовании Twisted? Это действительно очень хорошо для работы с асинхронными вещами. Он предоставляет основной цикл, и, например, вы можете использовать reactor.callFromThread, чтобы делать именно то, что вы хотите - запускать функцию в основном (реакторном) потоке.   -  person Claudiu    schedule 24.09.2013
comment
Twisted определенно круто смотрится. Однако я не уверен, что смогу заставить его работать с .NET Remoting, поскольку Twisted, похоже, хочет управлять конечными точками, и это то, что .NET Remoting делает для меня. После установления двусторонней связи я просто подписываюсь на событие на сервере и получаю свои обратные вызовы. В любом случае, если отбросить тупик, как мне настроить очередь имен функций, которые записываются фиктивным потоком и потребляются основным потоком?   -  person Jim C    schedule 24.09.2013
comment
Я вставлю это в ответ   -  person Claudiu    schedule 24.09.2013


Ответы (1)


Вы хотите использовать очередь (теперь queue из класса python 3) для создания очереди, которую ваши фиктивные потоки заполняют функциями и которую использует ваш основной поток.

import Queue

#somewhere accessible to both:
callback_queue = Queue.Queue()

def from_dummy_thread(func_to_call_from_main_thread):
    callback_queue.put(func_to_call_from_main_thread)

def from_main_thread_blocking():
    callback = callback_queue.get() #blocks until an item is available
    callback()

def from_main_thread_nonblocking():
    while True:
        try:
            callback = callback_queue.get(False) #doesn't block
        except Queue.Empty: #raised when queue is empty
            break
        callback()

Демо:

import threading
import time

def print_num(dummyid, n):
    print "From %s: %d" % (dummyid, n)
def dummy_run(dummyid):
    for i in xrange(5):
        from_dummy_thread(lambda: print_num(dummyid, i))
        time.sleep(0.5)
    
threading.Thread(target=dummy_run, args=("a",)).start()
threading.Thread(target=dummy_run, args=("b",)).start()

while True:
    from_main_thread_blocking()

Печать:

From a: 0
From b: 0
From a: 1
From b: 1
From b: 2
From a: 2
From b: 3
From a: 3
From b: 4
From a: 4

а потом блокирует навсегда

person Claudiu    schedule 24.09.2013
comment
Очень круто. Python упрощает передачу и хранение делегатов. Я ценю ваш очень подробный ответ. - person Jim C; 25.09.2013
comment
@JimC: отчасти поэтому это мой любимый язык =). ваше здоровье - person Claudiu; 25.09.2013