Saya sedang menulis aplikasi Python + GObject yang perlu membaca sejumlah data non-sepele dari disk saat mulai. Data dibaca secara serempak dan dibutuhkan sekitar 10 detik untuk menyelesaikan operasi baca, selama itu waktu pemuatan UI tertunda.
Saya ingin menjalankan tugas secara tidak sinkron, dan mendapatkan notifikasi ketika sudah siap, tanpa memblokir UI, kurang lebih seperti:
def take_ages():
read_a_huge_file_from_disk()
def on_finished_long_task():
print "Finished!"
run_long_task(task=take_ages, callback=on_finished_long_task)
load_the_UI_without_blocking_on_long_task()
Saya telah menggunakan GTask di masa lalu untuk hal semacam ini, tetapi saya khawatir bahwa kodenya belum tersentuh dalam 3 tahun, apalagi telah diporting ke GObject Introspection. Yang terpenting, ini tidak lagi tersedia di Ubuntu 12.04. Jadi saya mencari cara mudah untuk menjalankan tugas secara tidak sinkron, baik dengan cara Python standar atau dengan cara standar GObject / GTK +.
Sunting: ini beberapa kode dengan contoh apa yang saya coba lakukan. Saya sudah mencoba python-defer
seperti yang disarankan dalam komentar, tetapi saya tidak bisa mengelola untuk menjalankan tugas panjang secara tidak sinkron dan membiarkan UI memuat tanpa harus menunggu sampai selesai. Telusuri kode uji .
Apakah ada cara yang mudah dan banyak digunakan untuk menjalankan tugas yang tidak sinkron dan mendapatkan pemberitahuan setelah selesai?
sumber
async_call
fungsi Anda mungkin yang saya butuhkan. Maukah Anda mengembangkannya sedikit dan menambahkan jawaban, sehingga saya dapat menerimanya dan menghargai Anda setelah saya mengujinya? Terima kasih!Jawaban:
Masalah Anda adalah masalah yang sangat umum, oleh karena itu ada banyak solusi (gudang, antrian dengan multiprosesing atau threading, kolam pekerja, ...)
Karena sangat umum, ada juga solusi built-in python (dalam 3.2, tetapi didukung di sini: http://pypi.python.org/pypi/futures ) disebut concurrent.futures. 'Futures' tersedia dalam banyak bahasa, oleh karena itu python menyebutnya sama. Inilah beberapa panggilan umum (dan ini adalah contoh lengkap Anda , namun, bagian db digantikan oleh tidur, lihat alasannya di bawah).
Sekarang untuk masalah Anda, yang jauh lebih rumit daripada contoh sederhana yang Anda sarankan. Secara umum Anda memiliki utas atau proses untuk menyelesaikan ini, tetapi inilah mengapa contoh Anda sangat rumit:
slow_load
dari DB tidak dapat dipilih, yang berarti bahwa mereka tidak bisa begitu saja dilewatkan di antara proses. Jadi: jangan multiprocessing dengan hasil softwarecenter!print
, tidak ada perubahan status gtk, kecuali menambahkan panggilan balik!threads_init
, dan jika Anda memanggil gtk atau metode sama, Anda harus melindungi metode yang (di versi sebelumnya inigtk.gdk.threads_enter()
,gtk.gdk.threads_leave()
lihat misalnya gstreamer:. Http://pygstdocs.berlios.de/pygst-tutorial/playbin. html ).Saya dapat memberi Anda saran berikut:
slow_load
untuk mengembalikan hasil yang dapat dipilih dan menggunakan futures dengan proses.Sebagai catatan: solusi yang diberikan oleh orang lain (
Gio.io_scheduler_push_job
,async_call
) melakukan pekerjaan dengantime.sleep
tapi tidak dengansoftwarecenter.db
. Ini, karena semuanya bermuara pada utas atau proses dan utas untuk tidak bekerja dengan gtk dansoftwarecenter
.sumber
Berikut opsi lain menggunakan Penjadwal I / O GIO (Saya belum pernah menggunakannya sebelumnya dari Python, tetapi contoh di bawah ini tampaknya berjalan dengan baik).
sumber
Anda juga dapat menggunakan GLib.idle_add (callback) untuk memanggil tugas yang sudah berjalan lama setelah GLib Mainloop menyelesaikan semua itu acara dengan prioritas lebih tinggi (yang saya percaya termasuk membangun UI).
sumber
callback
dipanggil, itu akan dilakukan secara serempak, sehingga memblokir UI, kan?idle_add
adalah bahwa nilai kembali dari masalah panggilan balik. Jika itu benar, itu akan dipanggil lagi.Gunakan introspected
Gio
API untuk membaca file, dengan metode asynchronous, dan ketika membuat panggilan awal, melakukannya sebagai batas waktu denganGLib.timeout_add_seconds(3, call_the_gio_stuff)
manacall_the_gio_stuff
adalah fungsi yang kembaliFalse
.Timeout di sini perlu ditambahkan (meskipun, jumlah detik yang berbeda mungkin diperlukan), karena sementara panggilan Gio async bersifat asinkron, mereka tidak non-blocking, yang berarti bahwa aktivitas hard disk yang berat membaca file besar, atau besar jumlah file, dapat mengakibatkan UI diblokir, karena UI dan I / O masih dalam utas (utama) yang sama.
Jika Anda ingin menulis fungsi Anda sendiri sebagai async, dan berintegrasi dengan loop utama, menggunakan file I / O API Python, Anda harus menulis kode sebagai GObject, atau meneruskan panggilan balik, atau menggunakan
python-defer
untuk membantu Anda lakukan. Tapi yang terbaik adalah menggunakan Gio di sini, karena dapat membawa Anda banyak fitur bagus, terutama jika Anda melakukan membuka / menyimpan file di UX.sumber
Gio
API. Apa yang saya pikirkan adalah apakah ada cara untuk menjalankan tugas jangka panjang yang umum secara asinkron dengan cara yang sama seperti yang dilakukan GTask.Saya pikir perlu dicatat bahwa ini adalah cara berbelit-belit untuk melakukan apa yang disarankan @mhall.
Intinya, Anda menjalankan ini lalu menjalankan fungsi async_call.
Jika Anda ingin melihat cara kerjanya, Anda dapat bermain dengan pengatur waktu tidur dan terus mengklik tombol. Ini pada dasarnya sama dengan jawaban @ mhall kecuali bahwa ada kode contoh.
Berdasarkan ini yang bukan pekerjaan saya.
Catatan tambahan, Anda harus membiarkan utas lainnya selesai sebelum akan berakhir dengan benar atau memeriksa file. Buka utas anak Anda.
Edit ke alamat komentar:
Awalnya saya lupa
GObject.threads_init()
. Terbukti ketika tombol menyala, itu menginisialisasi threading untuk saya. Ini menutupi kesalahan saya.Umumnya aliran membuat jendela dalam memori, segera luncurkan utas lainnya, saat utas selesai perbarui tombol. Saya menambahkan tidur tambahan bahkan sebelum saya menelepon Gtk.main untuk memverifikasi bahwa pembaruan lengkap BISA berjalan sebelum jendela bahkan ditarik. Saya juga berkomentar untuk memverifikasi bahwa peluncuran thread tidak menghalangi menggambar jendela sama sekali.
sumber
slow_load
akan dieksekusi segera setelah UI dimulai, tetapi tampaknya tidak pernah dipanggil, kecuali tombol diklik, yang sedikit membingungkan saya, karena saya pikir tujuan tombol itu hanya untuk memberikan indikasi visual dari keadaan tugas.async_call
dalam contoh ini bekerja untuk saya, tetapi membawa kekacauan ketika saya porting ke aplikasi saya dan saya menambahkanslow_load
fungsi nyata yang saya punya.