Fungsi di foo
bawah ini mengembalikan string 'foo'
. Bagaimana saya bisa mendapatkan nilai 'foo'
yang dikembalikan dari target utas?
from threading import Thread
def foo(bar):
print('hello {}'.format(bar))
return 'foo'
thread = Thread(target=foo, args=('world!',))
thread.start()
return_value = thread.join()
"Satu cara yang jelas untuk melakukannya", yang ditunjukkan di atas, tidak berfungsi: thread.join()
dikembalikan None
.
futures = [executor.submit(foo, param) for param in param_list]
Pesanan akan dipertahankan, dan keluar dariwith
akan memungkinkan pengumpulan hasil.[f.result() for f in futures]
FWIW,
multiprocessing
modul memiliki antarmuka yang bagus untuk ini menggunakanPool
kelas. Dan jika Anda ingin tetap menggunakan thread daripada proses, Anda bisa menggunakanmultiprocessing.pool.ThreadPool
kelas sebagai pengganti drop-in.sumber
multiprocess
, mereka tidak ada hubungannya dengan Proses.processes=1
lebih dari satu jika Anda memiliki lebih banyak utas!Salah satu cara yang saya lihat adalah untuk melewatkan objek yang bisa berubah, seperti daftar atau kamus, ke konstruktor utas, bersama dengan indeks atau pengidentifikasi lainnya. Utas kemudian dapat menyimpan hasilnya di slot khusus di objek itu. Sebagai contoh:
Jika Anda benar-benar ingin
join()
mengembalikan nilai balik dari fungsi yang dipanggil, Anda bisa melakukan ini denganThread
subkelas seperti berikut:Itu menjadi sedikit berbulu karena beberapa nama mangling, dan mengakses struktur data "pribadi" yang khusus untuk
Thread
implementasi ... tetapi berfungsi.Untuk python3
sumber
threading
, bukan perpustakaan yang berbeda untuk mencoba, ditambah batasan ukuran kumpulan memperkenalkan masalah potensial tambahan, yang terjadi dalam kasus saya.TypeError: __init__() takes from 1 to 6 positional arguments but 7 were given
. Adakah cara untuk memperbaikinya?_Thread__target
hal ini. Anda akan membuat siapa pun yang mencoba untuk mem-porting kode Anda ke python 3 membenci Anda sampai mereka mengetahui apa yang telah Anda lakukan (karena menggunakan fitur tidak berdokumen yang berubah antara 2 dan 3). Dokumentasikan kode Anda dengan baik.Jawaban Jake baik, tetapi jika Anda tidak ingin menggunakan threadpool (Anda tidak tahu berapa banyak thread yang Anda perlukan, tetapi buatlah sesuai kebutuhan) maka cara yang baik untuk mengirimkan informasi antara thread adalah bawaannya. Kelas antrian , karena menawarkan keamanan utas.
Saya membuat dekorator berikut untuk membuatnya bertindak serupa dengan threadpool:
Maka Anda cukup menggunakannya sebagai:
Fungsi dihiasi membuat utas baru setiap kali dipanggil dan mengembalikan objek Utas yang berisi antrian yang akan menerima hasilnya.
MEMPERBARUI
Sudah cukup lama sejak saya memposting jawaban ini, tetapi masih mendapat tampilan jadi saya pikir saya akan memperbaruinya untuk mencerminkan cara saya melakukan ini dalam versi Python yang lebih baru:
Python 3.2 ditambahkan dalam
concurrent.futures
modul yang menyediakan antarmuka tingkat tinggi untuk tugas paralel. Ini menyediakanThreadPoolExecutor
danProcessPoolExecutor
, sehingga Anda dapat menggunakan utas atau kumpulan proses dengan api yang sama.Satu keuntungan dari api ini adalah mengirimkan tugas ke objek yang
Executor
dikembalikanFuture
, yang akan lengkap dengan nilai balik dari callable yang Anda kirimkan.Ini membuat melampirkan
queue
objek tidak perlu, yang menyederhanakan dekorator:Ini akan menggunakan pelaksana threadpool modul default jika tidak ada yang masuk
Penggunaannya sangat mirip dengan sebelumnya:
Jika Anda menggunakan Python 3.4+, satu fitur yang sangat bagus dari menggunakan metode ini (dan objek Future pada umumnya) adalah bahwa future yang dikembalikan dapat dibungkus untuk mengubahnya menjadi
asyncio.Future
withasyncio.wrap_future
. Ini membuatnya bekerja dengan mudah dengan coroutine:Jika Anda tidak memerlukan akses ke
concurrent.Future
objek yang mendasarinya , Anda dapat menyertakan bungkus di dekorator:Kemudian, setiap kali Anda perlu mendorong kode intensif atau memblokir cpu dari untaian loop acara, Anda dapat meletakkannya dalam fungsi yang didekorasi:
sumber
AttributeError: 'module' object has no attribute 'Lock'
ini berasal dari garisy = long_task(10)
... pemikiran?Solusi lain yang tidak perlu mengubah kode Anda yang ada:
Itu juga dapat dengan mudah disesuaikan dengan lingkungan multi-utas:
sumber
from queue import Queue
.Parris / kindall's answer
join
/return
answer porting ke Python 3:Catatan,
Thread
kelas diimplementasikan secara berbeda di Python 3.sumber
Saya mencuri jawaban baik hati dan membersihkannya sedikit saja.
Bagian kuncinya adalah menambahkan * args dan ** kwargs untuk bergabung () untuk menangani batas waktu
JAWABAN DIPERBARUI DI BAWAH INI
Ini adalah jawaban saya yang paling populer, jadi saya memutuskan untuk memperbarui dengan kode yang akan berjalan pada py2 dan py3.
Selain itu, saya melihat banyak jawaban untuk pertanyaan ini yang menunjukkan kurangnya pemahaman tentang Thread.join (). Beberapa gagal menangani
timeout
arg. Tetapi ada juga kasus sudut yang harus Anda ketahui tentang instance ketika Anda memiliki (1) fungsi target yang dapat kembaliNone
dan (2) Anda juga memberikantimeout
argumen untuk bergabung (). Silakan lihat "TEST 4" untuk memahami kasus sudut ini.Kelas ThreadWithReturn yang bekerja dengan py2 dan py3:
Beberapa tes sampel ditunjukkan di bawah ini:
Bisakah Anda mengidentifikasi kasus sudut yang mungkin kita temui dengan TEST 4?
Masalahnya adalah kita mengharapkan giveMe () mengembalikan None (lihat TEST 2), tetapi kami juga berharap join () mengembalikan None jika itu habis.
returned is None
berarti:(1) itulah yang mengembalikan giveMe (), atau
(2) gabung () habis
Contoh ini sepele karena kita tahu bahwa giveMe () akan selalu mengembalikan None. Tetapi dalam contoh dunia nyata (di mana target dapat secara sah mengembalikan Tidak ada atau sesuatu yang lain) kami ingin secara eksplisit memeriksa apa yang terjadi.
Di bawah ini adalah cara mengatasi casing sudut ini:
sumber
target
,args
dankwargs
argumen untuk init sebagai variabel anggota di kelas Anda.Menggunakan Antrian:
sumber
out_queue1
Anda harus mengulangout_queue1.get()
dan menangkap antrian. Pengecualianret = [] ; try: ; while True; ret.append(out_queue1.get(block=False)) ; except Queue.Empty: ; pass
. Semi-titik dua untuk mensimulasikan garis putus.Solusi saya untuk masalah ini adalah untuk membungkus fungsi dan utas dalam sebuah kelas. Tidak perlu menggunakan kumpulan, antrian, atau variabel lewat tipe c. Ini juga bukan pemblokiran. Anda malah memeriksa status. Lihat contoh cara menggunakannya di akhir kode.
sumber
join
selalu kembaliNone
, saya pikir Anda harus subclassThread
untuk menangani kode pengembalian dan sebagainya.sumber
Mempertimbangkan komentar @iman pada jawaban @JakeBiesinger, saya telah mengomposisikan ulang untuk memiliki sejumlah utas:
Bersulang,
Orang.
sumber
Anda dapat mendefinisikan mutable di atas lingkup fungsi berulir, dan menambahkan hasilnya. (Saya juga memodifikasi kode agar kompatibel dengan python3)
Ini kembali
{'world!': 'foo'}
Jika Anda menggunakan input fungsi sebagai kunci untuk hasil Anda, setiap input unik dijamin untuk memberikan entri dalam hasil
sumber
Saya menggunakan pembungkus ini, yang dengan nyaman mengubah fungsi apa pun untuk berjalan dalam
Thread
- menjaga nilai pengembalian atau pengecualiannya. Itu tidak menambahQueue
overhead.Contoh Penggunaan
Catatan tentang
threading
modulNilai pengembalian yang nyaman & penanganan perkecualian pada fungsi berulir adalah kebutuhan "Pythonic" yang sering dan seharusnya memang sudah ditawarkan oleh
threading
modul - mungkin langsung diThread
kelas standar .ThreadPool
memiliki terlalu banyak overhead untuk tugas-tugas sederhana - 3 mengelola utas, banyak birokrasi. SayangnyaThread
tata letak disalin dari Jawa awalnya - yang Anda lihat misalnya dari parameter konstruktor 1st (!) Yang masih tidak bergunagroup
.sumber
Tentukan target Anda untuk
1) mengambil argumen
q
2) ganti pernyataan apa pun
return foo
denganq.put(foo); return
jadi fungsi
akan menjadi
dan kemudian Anda akan melanjutkannya
Dan Anda dapat menggunakan fungsi dekorator / pembungkus untuk membuatnya sehingga Anda dapat menggunakan fungsi yang ada
target
tanpa mengubahnya, tetapi ikuti skema dasar ini.sumber
results = [ans_q.get() for _ in xrange(len(threads))]
Seperti yang disebutkan multiprocessing pool jauh lebih lambat daripada dasar threading. Menggunakan antrian seperti yang diusulkan dalam beberapa jawaban di sini adalah alternatif yang sangat efektif. Saya telah menggunakannya dengan kamus untuk dapat menjalankan banyak utas kecil dan memulihkan beberapa jawaban dengan menggabungkannya dengan kamus:
sumber
Ide GuySoft bagus, tapi saya pikir objek tidak harus mewarisi dari Thread dan mulai () dapat dihapus dari antarmuka:
sumber
Salah satu solusi yang biasa adalah membungkus fungsi Anda
foo
dengan dekorator sepertiMaka seluruh kode mungkin terlihat seperti itu
Catatan
Satu masalah penting adalah bahwa nilai-nilai kembali mungkin tidak teratur . (Bahkan,
return value
tidak harus disimpan kequeue
, karena Anda dapat memilih struktur data aman- sewenang - wenang )sumber
Mengapa tidak menggunakan variabel global saja?
sumber
Jawaban Kindall dalam Python3
sumber
Jika hanya Benar atau Salah yang akan divalidasi dari panggilan fungsi, solusi sederhana yang saya temukan adalah memperbarui daftar global.
Ini lebih bermanfaat jika Anda ingin mengetahui apakah ada salah satu utas yang mengembalikan status palsu untuk mengambil tindakan yang diperlukan.
sumber