Seu problema é muito comum, por isso existem toneladas de soluções (galpões, filas com multiprocessamento ou encadeamento, pools de trabalho, etc.)
Como é tão comum, há também uma solução integrada em Python (na versão 3.2, mas backported aqui: link ) chamado concorrente.futures. 'Futuros' estão disponíveis em muitos idiomas, portanto python os chama da mesma forma. Aqui estão as chamadas típicas (e aqui está o seu exemplo completo , no entanto, a parte db é substituída por sleep, veja abaixo por que) .
from concurrent import futures
executor = futures.ProcessPoolExecutor(max_workers=1)
#executor = futures.ThreadPoolExecutor(max_workers=1)
future = executor.submit(slow_load)
future.add_done_callback(self.on_complete)
Agora, para o seu problema, que é muito mais complicado do que o seu exemplo simples sugere. Em geral, você tem threads ou processos para resolver isso, mas eis porque seu exemplo é tão complicado:
- A maioria das implementações em Python tem um GIL, o que faz com que os threads não utilizem totalmente os multicores. Então: não use threads com python!
- Os objetos que você deseja retornar em
slow_load
do banco de dados não são pickelable, o que significa que eles não podem simplesmente ser transmitidos entre processos. Então: não há multiprocessamento com resultados softwarecenter! - A biblioteca que você chama (softwarecenter.db) não é threadsafe (parece incluir gtk ou similar), portanto, chamar esses métodos em um encadeamento resulta em um comportamento estranho (no meu teste, tudo desde 'funciona' por 'core dump' 'para simplesmente desistir sem resultados). Então: sem threads com softwarecenter.
- Todo retorno de chamada assíncrono no gtk não deve fazer qualquer coisa , exceto o sheduling de um retorno de chamada, que será chamado no mainloop simplificado. Então: não
print
, nenhum estado gtk muda, exceto adicionar um retorno de chamada! - Gtk e similares não funcionam com threads prontos para uso. Você precisa fazer
threads_init
, e se você chamar um método gtk ou similar, você tem que proteger esse método (em versões anteriores eragtk.gdk.threads_enter()
,gtk.gdk.threads_leave()
. Veja por exemplo gstreamer: link ).
Eu posso lhe dar a seguinte sugestão:
- Reescreva seu
slow_load
para retornar resultados pickelable e use futuros com processos. - Mude de softwarecenter para python-apt ou similar (provavelmente você não gosta disso). Mas, como você é funcionário da Canonical, pode pedir diretamente aos desenvolvedores do softwarecenter para adicionar documentação ao software (por exemplo, declarar que não é thread safe) e, melhor ainda, tornar o softwarecenter thread-safe.
Como nota: as soluções fornecidas pelos outros ( Gio.io_scheduler_push_job
, async_call
) fazem funcionar com time.sleep
, mas não com softwarecenter.db
. Isto é, porque tudo se resume a threads ou processos e threads para não funcionar com gtk e softwarecenter
.