Python'da bir iş parçacığından dönüş değeri nasıl alınır?


342

Aşağıdaki işlev foobir dize döndürür 'foo'. Değeri nasıl alabilirim'foo' dizisinin hedefinden döndürülen ?

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()

Yukarıda gösterilen "bunu yapmanın bariz bir yolu" çalışmıyor: thread.join()iade None.

Yanıtlar:


37

Python 3.2 ve üzeri sürümlerde, stdlib concurrent.futuresmodülü, threadingbir iş parçacığından ana iş parçacığına geri dönüş değerlerinin veya istisnaların aktarılması da dahil olmak üzere daha yüksek düzeyde bir API sağlar :

import concurrent.futures

def foo(bar):
    print('hello {}'.format(bar))
    return 'foo'

with concurrent.futures.ThreadPoolExecutor() as executor:
    future = executor.submit(foo, 'world!')
    return_value = future.result()
    print(return_value)

Bunu merak edenler için bir konu listesi ile yapılabilir. futures = [executor.submit(foo, param) for param in param_list]Sipariş korunacak ve iadeden çıkılması withsonuç toplamaya izin verecektir. [f.result() for f in futures]
jayreed1

273

FWIW, multiprocessingmodül Poolsınıfı kullanarak bunun için hoş bir arayüze sahip . İşlemler yerine iş parçacıklarına sadık kalmak istiyorsanız, multiprocessing.pool.ThreadPoolsınıfı bir bırakma yedeği olarak kullanabilirsiniz.

def foo(bar, baz):
  print 'hello {0}'.format(bar)
  return 'foo' + baz

from multiprocessing.pool import ThreadPool
pool = ThreadPool(processes=1)

async_result = pool.apply_async(foo, ('world', 'foo')) # tuple of args for foo

# do some other stuff in the main process

return_val = async_result.get()  # get the return value from your function.

50
@JakeBiesinger Demek istediğim, cevap aradığım, Konudan nasıl yanıt alacağım, buraya geldi ve kabul edilen cevap belirtilen soruya cevap vermiyor. Konuları ve süreçleri ayırırım. Global Tercüman Kilidi hakkında biliyorum ama G / Ç bağlı sorun üzerinde çalışıyorum, bu yüzden Konular tamam, süreçlere ihtiyacım yok. Burada diğer cevaplar daha iyi cevap soru belirtildi.
omikron

7
@omikron Ancak bu işlevi etkinleştiren bir alt sınıf kullanmadığınız sürece python'daki iş parçacıkları yanıt döndürmez. Olası alt sınıflardan, ThreadPools mükemmel bir seçimdir (thread sayısı seçin, map / sync / async ile uygula). İthal multiprocessedilmelerine rağmen , Süreçlerle hiçbir ilgisi yoktur.
Jake Biesinger

4
@JakeBiesinger Oh, körüm. Gereksiz yorumlarım için özür dilerim. Haklısın. Az önce çoklu işlem = süreçler olduğunu varsaydım.
omikron

12
Daha processes=1fazla iş parçacığınız varsa birden fazla ayarlamayı unutmayın !
iman

4
Çoklu işlem ve iş parçacığı havuzu ile ilgili sorun, temel iş parçacığı kitaplığı ile karşılaştırıldığında iş parçacıklarını ayarlamak ve başlatmak çok daha yavaş olmasıdır. Uzun koşu iplikleri başlatmak için harika, ancak çok sayıda kısa koşu ipliği başlatmak gerektiğinde amacı yenmek. Burada diğer yanıtlarda belgelenen "iş parçacığı" ve "Kuyruk" kullanmanın çözümü, bence bu son kullanım durumu için daha iyi bir alternatiftir.
Yves Dorfsman

243

Gördüğüm bir yol, liste veya sözlük gibi değiştirilebilir bir nesneyi bir dizin veya başka tür bir tanımlayıcıyla birlikte iş parçacığının yapıcısına iletmektir. İş parçacığı daha sonra sonuçlarını bu nesnedeki özel yuvasında saklayabilir. Örneğin:

def foo(bar, result, index):
    print 'hello {0}'.format(bar)
    result[index] = "foo"

from threading import Thread

threads = [None] * 10
results = [None] * 10

for i in range(len(threads)):
    threads[i] = Thread(target=foo, args=('world!', results, i))
    threads[i].start()

# do some other stuff

for i in range(len(threads)):
    threads[i].join()

print " ".join(results)  # what sound does a metasyntactic locomotive make?

join()Aranan işlevin dönüş değerini gerçekten döndürmek istiyorsanız, bunu Threadaşağıdaki gibi bir alt sınıfla yapabilirsiniz :

from threading import Thread

def foo(bar):
    print 'hello {0}'.format(bar)
    return "foo"

class ThreadWithReturnValue(Thread):
    def __init__(self, group=None, target=None, name=None,
                 args=(), kwargs={}, Verbose=None):
        Thread.__init__(self, group, target, name, args, kwargs, Verbose)
        self._return = None
    def run(self):
        if self._Thread__target is not None:
            self._return = self._Thread__target(*self._Thread__args,
                                                **self._Thread__kwargs)
    def join(self):
        Thread.join(self)
        return self._return

twrv = ThreadWithReturnValue(target=foo, args=('world!',))

twrv.start()
print twrv.join()   # prints foo

Bu, bazı ad yönetimi nedeniyle biraz kıllı olur ve Threaduygulamaya özgü "özel" veri yapılarına erişir ... ancak işe yarar.

Python3 için

class ThreadWithReturnValue(Thread):
    def __init__(self, group=None, target=None, name=None,
                 args=(), kwargs={}, Verbose=None):
        Thread.__init__(self, group, target, name, args, kwargs)
        self._return = None
    def run(self):
        print(type(self._target))
        if self._target is not None:
            self._return = self._target(*self._args,
                                                **self._kwargs)
    def join(self, *args):
        Thread.join(self, *args)
        return self._return

37
güzel, örnek için teşekkürler! i neden ilk başta bir dönüş değeri işleme ile uygulanmadığını merak ediyorum, desteklemek için yeterince açık bir şey gibi görünüyor.
wim

16
Bence bu kabul edilen cevap olmalı - OP istedi threading, denemek için farklı bir kütüphane değil, havuz büyüklüğü sınırlaması da benim durumumda meydana gelen ek bir potansiyel problemi ortaya çıkarıyor.
domoarigato

10
Büyük tren şakası.
meawoppl

7
Python3'te bu geri döner TypeError: __init__() takes from 1 to 6 positional arguments but 7 were given. Bunu düzeltmenin bir yolu var mý?
GuySoft

2
Bunlardan ikincisini (cazibesini) yapmak isteyenler için uyarı _Thread__target. Kodunuzu python 3'e aktarmaya çalışan herkesin, yaptığınız işi çözene kadar nefret etmesini sağlayabilirsiniz (2 ile 3 arasında değişen belgelenmemiş özellikler kullanması nedeniyle). Kodunuzu iyi belgeleyin.
Ben Taylor

84

Jake'in cevabı iyidir, ancak bir threadpool kullanmak istemiyorsanız (kaç tane thread'a ihtiyacınız olduğunu bilmiyorsanız, ancak gerektiğinde oluşturun), o zaman threadlar arasında bilgi aktarmanın iyi bir yolu yerleşiktir. İş parçacığı güvenliği sunduğu için Queue.Queue sınıfı.

Ben threadpool benzer bir şekilde hareket yapmak için aşağıdaki dekoratör yarattı:

def threaded(f, daemon=False):
    import Queue

    def wrapped_f(q, *args, **kwargs):
        '''this function calls the decorated function and puts the 
        result in a queue'''
        ret = f(*args, **kwargs)
        q.put(ret)

    def wrap(*args, **kwargs):
        '''this is the function returned from the decorator. It fires off
        wrapped_f in a new thread and returns the thread object with
        the result queue attached'''

        q = Queue.Queue()

        t = threading.Thread(target=wrapped_f, args=(q,)+args, kwargs=kwargs)
        t.daemon = daemon
        t.start()
        t.result_queue = q        
        return t

    return wrap

Sonra sadece şu şekilde kullanın:

@threaded
def long_task(x):
    import time
    x = x + 5
    time.sleep(5)
    return x

# does not block, returns Thread object
y = long_task(10)
print y

# this blocks, waiting for the result
result = y.result_queue.get()
print result

Süslü işlev her çağrıldığında yeni bir iş parçacığı oluşturur ve sonucu alacak kuyruğu içeren bir Thread nesnesi döndürür.

GÜNCELLEME

Bu yanıtı yayınladığımdan beri oldukça uzun bir süre oldu, ancak yine de görüntüleme alıyor, bu yüzden Python'un yeni sürümlerinde bunu yapmamı yansıtacak şekilde güncelleyeceğimi düşündüm:

Python 3.2 concurrent.futures, paralel görevler için üst düzey bir arayüz sağlayan modüle eklendi . Aynı API ile bir iş parçacığı veya işlem havuzu kullanabilirsiniz ThreadPoolExecutorve sağlar ProcessPoolExecutor.

Bu API bir faydası şudur bir bir görev göndererek bu Executorgetiriler bir FutureGönderdiğiniz çağrılabilir dönüş değeri ile tamamlayacak nesneyi.

Bu, bir queuenesneyi eklemeyi gereksiz kılar , bu da dekoratörü biraz basitleştirir:

_DEFAULT_POOL = ThreadPoolExecutor()

def threadpool(f, executor=None):
    @wraps(f)
    def wrap(*args, **kwargs):
        return (executor or _DEFAULT_POOL).submit(f, *args, **kwargs)

    return wrap

Bu, biri aktarılmazsa varsayılan modül threadpool yürütücüsünü kullanır .

Kullanım öncekine çok benzer:

@threadpool
def long_task(x):
    import time
    x = x + 5
    time.sleep(5)
    return x

# does not block, returns Future object
y = long_task(10)
print y

# this blocks, waiting for the result
result = y.result()
print result

Python 3.4+ kullanıyorsanız, bu yöntemi (ve genel olarak Future nesnelerini) kullanmanın gerçekten güzel bir özelliği, döndürülen geleceğin bir asyncio.Futureile dönüştürülmesi için sarılabilmesidir asyncio.wrap_future. Bu, programlarla kolayca çalışmasını sağlar:

result = await asyncio.wrap_future(long_task(10))

Temel alınan concurrent.Futurenesneye erişmeniz gerekmiyorsa , sargıyı dekoratöre ekleyebilirsiniz:

_DEFAULT_POOL = ThreadPoolExecutor()

def threadpool(f, executor=None):
    @wraps(f)
    def wrap(*args, **kwargs):
        return asyncio.wrap_future((executor or _DEFAULT_POOL).submit(f, *args, **kwargs))

    return wrap

Daha sonra, cpu yoğun veya engelleme kodunu olay döngüsü iş parçacığından zorlamanız gerektiğinde, dekore edilmiş bir işleve koyabilirsiniz:

@threadpool
def some_long_calculation():
    ...

# this will suspend while the function is executed on a threadpool
result = await some_long_calculation()

Bunun işe yaramasını sağlayamıyorum; Bunun AttributeError: 'module' object has no attribute 'Lock'hattan yayıldığını belirten bir hata alıyorum y = long_task(10)... düşünceler?
sadmicrowave

1
Kod açıkça Lock kullanmıyor, bu nedenle sorun kodunuzda başka bir yerde olabilir. Bu konuda yeni bir SO sorusu göndermek isteyebilirsiniz
bj0

Results_queue neden bir örnek özelliği? Bir sınıf özniteliği olsaydı daha iyi olurdu, böylece kullanıcılar açık ve belirsiz olmayan @threaded'i kullanırken sonuç_hutuşunu çağırmak zorunda kalmazlar mı?
nonbot

@ t88, ne demek istediğinizi bilmiyorum, sonuca erişmenin bir yoluna ihtiyacınız var, yani ne arayacağınızı bilmeniz gerekiyor. Bunun başka bir şey olmasını istiyorsanız, Thread'ı alt sınıflandırabilir ve istediğinizi yapabilirsiniz (bu basit bir çözümdü).
Kuyruğun

1
Bu harika! Çok teşekkür ederim.
Ganesh Kathiresan

53

Mevcut kodunuzu değiştirmenizi gerektirmeyen başka bir çözüm:

import Queue
from threading import Thread

def foo(bar):
    print 'hello {0}'.format(bar)
    return 'foo'

que = Queue.Queue()

t = Thread(target=lambda q, arg1: q.put(foo(arg1)), args=(que, 'world!'))
t.start()
t.join()
result = que.get()
print result

Ayrıca çok iş parçacıklı bir ortama kolayca ayarlanabilir:

import Queue
from threading import Thread

def foo(bar):
    print 'hello {0}'.format(bar)
    return 'foo'

que = Queue.Queue()
threads_list = list()

t = Thread(target=lambda q, arg1: q.put(foo(arg1)), args=(que, 'world!'))
t.start()
threads_list.append(t)

# Add more threads here
...
threads_list.append(t2)
...
threads_list.append(t3)
...

# Join all the threads
for t in threads_list:
    t.join()

# Check thread's return value
while not que.empty():
    result = que.get()
    print result

t = İş parçacığı (target = lambda q, arg1: q.put (foo (arg1)), args = (que, 'world!')) q.put burada ne yapıyor, Queue.Queue () ne yapıyor
vijay shanker

6
Memleketinizde bir heykel olmalı, teşekkürler!
Onilol

3
@Onilol - Çok teşekkür ederim.
Arik

4
Python3 için, değiştirmeniz gerekiyor from queue import Queue.
Gino Mempin

1
Geri dönüş değerinin ana iş parçacığına geri dönmesine izin vermek için bu en az yıkıcı yöntem (orijinal kod tabanını önemli ölçüde yeniden yapılandırmaya gerek yoktur) gibi görünüyor.
Fanchen Bao

24

Parris / tür cevap join / returnPython 3'e verilen cevap:

from threading import Thread

def foo(bar):
    print('hello {0}'.format(bar))
    return "foo"

class ThreadWithReturnValue(Thread):
    def __init__(self, group=None, target=None, name=None, args=(), kwargs=None, *, daemon=None):
        Thread.__init__(self, group, target, name, args, kwargs, daemon=daemon)

        self._return = None

    def run(self):
        if self._target is not None:
            self._return = self._target(*self._args, **self._kwargs)

    def join(self):
        Thread.join(self)
        return self._return


twrv = ThreadWithReturnValue(target=foo, args=('world!',))

twrv.start()
print(twrv.join())   # prints foo

Not, Threadsınıf Python 3'te farklı uygulanır.


1
join, iletilmesi gereken bir zaman aşımı parametresi alır
cz

22

Biraz cevap verdim ve biraz temizledim.

Anahtar kısım zaman aşımını işlemek için katılmak için * args ve ** kwargs () eklemektir.

class threadWithReturn(Thread):
    def __init__(self, *args, **kwargs):
        super(threadWithReturn, self).__init__(*args, **kwargs)

        self._return = None

    def run(self):
        if self._Thread__target is not None:
            self._return = self._Thread__target(*self._Thread__args, **self._Thread__kwargs)

    def join(self, *args, **kwargs):
        super(threadWithReturn, self).join(*args, **kwargs)

        return self._return

AŞAĞIDAKİ GÜNCEL CEVAP

Bu benim en popüler upvoted cevap, bu yüzden hem py2 hem de py3 üzerinde çalışacak kod ile güncelleme karar verdi.

Ayrıca, Thread.join () ile ilgili anlama eksikliği gösteren bu soruya birçok cevap görüyorum. Bazıları timeoutargümanı tamamen ele alamıyor. Ancak (1) geri dönebilen bir hedef fonksiyona sahip olduğunuz Noneve (2)timeout join () öğesine katılmak argümanı . Bu köşe durumunu anlamak için lütfen "TEST 4" e bakınız.

Py2 ve py3 ile çalışan ThreadWithReturn sınıfı:

import sys
from threading import Thread
from builtins import super    # https://stackoverflow.com/a/30159479

if sys.version_info >= (3, 0):
    _thread_target_key = '_target'
    _thread_args_key = '_args'
    _thread_kwargs_key = '_kwargs'
else:
    _thread_target_key = '_Thread__target'
    _thread_args_key = '_Thread__args'
    _thread_kwargs_key = '_Thread__kwargs'

class ThreadWithReturn(Thread):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self._return = None

    def run(self):
        target = getattr(self, _thread_target_key)
        if not target is None:
            self._return = target(
                *getattr(self, _thread_args_key),
                **getattr(self, _thread_kwargs_key)
            )

    def join(self, *args, **kwargs):
        super().join(*args, **kwargs)
        return self._return

Bazı örnek testler aşağıda gösterilmiştir:

import time, random

# TEST TARGET FUNCTION
def giveMe(arg, seconds=None):
    if not seconds is None:
        time.sleep(seconds)
    return arg

# TEST 1
my_thread = ThreadWithReturn(target=giveMe, args=('stringy',))
my_thread.start()
returned = my_thread.join()
# (returned == 'stringy')

# TEST 2
my_thread = ThreadWithReturn(target=giveMe, args=(None,))
my_thread.start()
returned = my_thread.join()
# (returned is None)

# TEST 3
my_thread = ThreadWithReturn(target=giveMe, args=('stringy',), kwargs={'seconds': 5})
my_thread.start()
returned = my_thread.join(timeout=2)
# (returned is None) # because join() timed out before giveMe() finished

# TEST 4
my_thread = ThreadWithReturn(target=giveMe, args=(None,), kwargs={'seconds': 5})
my_thread.start()
returned = my_thread.join(timeout=random.randint(1, 10))

TEST 4 ile karşılaşabileceğimiz köşe vakasını belirleyebilir misiniz?

Sorun şu ki, giveMe () 'in None döndürmesini bekliyoruz (bkz. TEST 2), ama ayrıca join ()' in zaman aşımı durumunda None döndürmesini bekliyoruz.

returned is None aşağıdakilerden biri anlamına gelir:

(1) giveMe () geri döndü, ya da

(2) join () zaman aşımına uğradı

Bu örnek önemsizdir çünkü giveMe () işlevinin her zaman None döndüreceğini biliyoruz. Ancak gerçek dünya örneğinde (hedefin meşru olarak Hiçbiri veya başka bir şey getirebileceği durumlarda), ne olduğunu açıkça kontrol etmek isteriz.

Bu köşe vakasının nasıl ele alınacağı aşağıda açıklanmıştır:

# TEST 4
my_thread = ThreadWithReturn(target=giveMe, args=(None,), kwargs={'seconds': 5})
my_thread.start()
returned = my_thread.join(timeout=random.randint(1, 10))

if my_thread.isAlive():
    # returned is None because join() timed out
    # this also means that giveMe() is still running in the background
    pass
    # handle this based on your app's logic
else:
    # join() is finished, and so is giveMe()
    # BUT we could also be in a race condition, so we need to update returned, just in case
    returned = my_thread.join()

Python3 için _Thread_target eşdeğerini biliyor musunuz? Bu özellik Python3'te mevcut değil.
GreySage

Threading.py dosyasına baktım, _target olduğu ortaya çıktı (diğer öznitelikler benzer şekilde adlandırılır).
GreySage

Kaydetmek eğer, iplik sınıfının özel değişkenleri erişen önlemek olabilir target, argsve kwargsargümanlar init Sınıfındaki üye değişkenler olarak.
Tolli

@GreySage Cevabımı gör, bu bloğu aşağıdaki
python3'e

@GreySage cevabı şimdi py2 ve py3'ü destekliyor
user2426679

15

Kuyruğu kullanma:

import threading, queue

def calc_square(num, out_queue1):
  l = []
  for x in num:
    l.append(x*x)
  out_queue1.put(l)


arr = [1,2,3,4,5,6,7,8,9,10]
out_queue1=queue.Queue()
t1=threading.Thread(target=calc_square, args=(arr,out_queue1))
t1.start()
t1.join()
print (out_queue1.get())

1
Gerçekten bu çözüm gibi, kısa ve tatlı. İşleviniz bir giriş kuyruğu okursa ve eklerseniz out_queue1, döngüye out_queue1.get()girmeniz ve Queue.Empty istisnasını yakalamanız gerekir ret = [] ; try: ; while True; ret.append(out_queue1.get(block=False)) ; except Queue.Empty: ; pass. Satır kesmelerini simüle etmek için noktalı virgül.
sastorsl

6

Benim sorunum çözüm bir işlev ve iş parçacığı sarmak için. Havuz, kuyruk veya c tipi değişken geçişi gerektirmez. Ayrıca tıkanmaz. Bunun yerine durumu kontrol edersiniz. Kodun sonunda nasıl kullanılacağına ilişkin örneğe bakın.

import threading

class ThreadWorker():
    '''
    The basic idea is given a function create an object.
    The object can then run the function in a thread.
    It provides a wrapper to start it,check its status,and get data out the function.
    '''
    def __init__(self,func):
        self.thread = None
        self.data = None
        self.func = self.save_data(func)

    def save_data(self,func):
        '''modify function to save its returned data'''
        def new_func(*args, **kwargs):
            self.data=func(*args, **kwargs)

        return new_func

    def start(self,params):
        self.data = None
        if self.thread is not None:
            if self.thread.isAlive():
                return 'running' #could raise exception here

        #unless thread exists and is alive start or restart it
        self.thread = threading.Thread(target=self.func,args=params)
        self.thread.start()
        return 'started'

    def status(self):
        if self.thread is None:
            return 'not_started'
        else:
            if self.thread.isAlive():
                return 'running'
            else:
                return 'finished'

    def get_results(self):
        if self.thread is None:
            return 'not_started' #could return exception
        else:
            if self.thread.isAlive():
                return 'running'
            else:
                return self.data

def add(x,y):
    return x +y

add_worker = ThreadWorker(add)
print add_worker.start((1,2,))
print add_worker.status()
print add_worker.get_results()

bir istisnayı nasıl ele alırsınız? diyelim ki add fonksiyonu verildi ve int ve str. tüm iş parçacığı başarısız olur veya sadece biri başarısız olur?
user1745713

4

joinher zaman geri dönmek None, ben Threaddönüş kodları ve böylece işlemek için alt sınıf gerektiğini düşünüyorum .


4

@JakeBiesinger cevap @iman yorum dikkate alınarak Ben çeşitli konuları için yeniden oluşturduk:

from multiprocessing.pool import ThreadPool

def foo(bar, baz):
    print 'hello {0}'.format(bar)
    return 'foo' + baz

numOfThreads = 3 
results = []

pool = ThreadPool(numOfThreads)

for i in range(0, numOfThreads):
    results.append(pool.apply_async(foo, ('world', 'foo'))) # tuple of args for foo)

# do some other stuff in the main process
# ...
# ...

results = [r.get() for r in results]
print results

pool.close()
pool.join()

Alkış,

İnsan.


2

İş parçacığı işlevi kapsamının üstünde bir değişken tanımlayabilir ve sonucu buna ekleyebilirsiniz. (Ayrıca kodu python3 uyumlu olacak şekilde değiştirdim)

returns = {}
def foo(bar):
    print('hello {0}'.format(bar))
    returns[bar] = 'foo'

from threading import Thread
t = Thread(target=foo, args=('world!',))
t.start()
t.join()
print(returns)

Bu geri döner {'world!': 'foo'}

İşlev girişini sonuç diktenizin anahtarı olarak kullanırsanız, her benzersiz girişin sonuçlara bir giriş vermesi garanti edilir


2

Ben Threaddönüş değeri veya istisna dikkat çekici bir - çalıştırmak için herhangi bir işlevi rahat döner bu sarmalayıcı kullanıyorum . QueueYükü eklemez .

def threading_func(f):
    """Decorator for running a function in a thread and handling its return
    value or exception"""
    def start(*args, **kw):
        def run():
            try:
                th.ret = f(*args, **kw)
            except:
                th.exc = sys.exc_info()
        def get(timeout=None):
            th.join(timeout)
            if th.exc:
                raise th.exc[0], th.exc[1], th.exc[2] # py2
                ##raise th.exc[1] #py3                
            return th.ret
        th = threading.Thread(None, run)
        th.exc = None
        th.get = get
        th.start()
        return th
    return start

Kullanım örnekleri

def f(x):
    return 2.5 * x
th = threading_func(f)(4)
print("still running?:", th.is_alive())
print("result:", th.get(timeout=1.0))

@threading_func
def th_mul(a, b):
    return a * b
th = th_mul("text", 2.5)

try:
    print(th.get())
except TypeError:
    print("exception thrown ok.")

threadingModül hakkında notlar

Konforlu dönüş değeri ve dişli bir fonksiyonun istisna yönetimi sık sık "Pitonik" bir ihtiyaçtır ve threadingmodül tarafından zaten sunulmalıdır - muhtemelen doğrudan standart Threadsınıfta. ThreadPoolbasit görevler için çok fazla ek yüke sahiptir - 3 iş parçacığı, çok sayıda bürokrasi. Ne yazık ki, Threadyerleşimi orijinal olarak Java'dan kopyalandı - örneğin hala yararsız 1. (!) Yapıcı parametresinden görüyorsunuz group.


ilk kurucu işe yaramaz değil, gelecekteki uygulama için orada ayrılmış .. python paralel programlama yemek kitabı
vijay shanker

1

İçin hedefinizi tanımlayın
belirtilirler) 1 q
) 2 herhangi bir ifade yerine return foosahipq.put(foo); return

yani bir fonksiyon

def func(a):
    ans = a * a
    return ans

olacaktı

def func(a, q):
    ans = a * a
    q.put(ans)
    return

ve sonra böyle devam edeceksin

from Queue import Queue
from threading import Thread

ans_q = Queue()
arg_tups = [(i, ans_q) for i in xrange(10)]

threads = [Thread(target=func, args=arg_tup) for arg_tup in arg_tups]
_ = [t.start() for t in threads]
_ = [t.join() for t in threads]
results = [q.get() for _ in xrange(len(threads))]

Ve bunu yapmak için işlev dekoratörleri / paketleyicileri kullanabilirsiniz, böylece mevcut işlevlerinizi targetdeğiştirmeden kullanabilirsiniz, ancak bu temel şemayı takip edin.


O olmalıresults = [ans_q.get() for _ in xrange(len(threads))]
Vahap H Kumar

1

Belirtildiği gibi, çok işlemli havuz temel iş parçacığından çok daha yavaştır. Burada bazı cevaplarda önerildiği gibi kuyrukları kullanmak çok etkili bir alternatiftir. Birçok küçük iş parçacığı çalıştırmak ve birden çok yanıtı sözlüklerle birleştirerek geri almak için sözlüklerle birlikte kullandım:

#!/usr/bin/env python3

import threading
# use Queue for python2
import queue
import random

LETTERS = 'abcdefghijklmnopqrstuvwxyz'
LETTERS = [ x for x in LETTERS ]

NUMBERS = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

def randoms(k, q):
    result = dict()
    result['letter'] = random.choice(LETTERS)
    result['number'] = random.choice(NUMBERS)
    q.put({k: result})

threads = list()
q = queue.Queue()
results = dict()

for name in ('alpha', 'oscar', 'yankee',):
    threads.append( threading.Thread(target=randoms, args=(name, q)) )
    threads[-1].start()
_ = [ t.join() for t in threads ]
while not q.empty():
    results.update(q.get())

print(results)

1

GuySoft'un fikri harika, ama nesnenin mutlaka Thread'den miras almak zorunda olmadığını ve start () arayüzünden kaldırılabileceğini düşünüyorum:

from threading import Thread
import queue
class ThreadWithReturnValue(object):
    def __init__(self, target=None, args=(), **kwargs):
        self._que = queue.Queue()
        self._t = Thread(target=lambda q,arg1,kwargs1: q.put(target(*arg1, **kwargs1)) ,
                args=(self._que, args, kwargs), )
        self._t.start()

    def join(self):
        self._t.join()
        return self._que.get()


def foo(bar):
    print('hello {0}'.format(bar))
    return "foo"

twrv = ThreadWithReturnValue(target=foo, args=('world!',))

print(twrv.join())   # prints foo

0

Her zamanki bir çözüm, fonksiyonunuzu foobir dekoratörle sarmaktır

result = queue.Queue()

def task_wrapper(*args):
    result.put(target(*args))

Sonra tüm kod böyle görünebilir

result = queue.Queue()

def task_wrapper(*args):
    result.put(target(*args))

threads = [threading.Thread(target=task_wrapper, args=args) for args in args_list]

for t in threads:
    t.start()
    while(True):
        if(len(threading.enumerate()) < max_num):
            break
for t in threads:
    t.join()
return result

Not

Önemli bir sorun, dönüş değerlerinin sınırsız olabilmesidir . (Aslında, iş parçacığı için güvenli veri yapısını seçebildiğiniz return valueiçin queue, mutlaka kaydedilmez )


0

Neden sadece global değişkeni kullanmıyorsunuz?

import threading


class myThread(threading.Thread):
    def __init__(self, ind, lock):
        threading.Thread.__init__(self)
        self.ind = ind
        self.lock = lock

    def run(self):
        global results
        with self.lock:
            results.append(self.ind)



results = []
lock = threading.Lock()
threads = [myThread(x, lock) for x in range(1, 4)]
for t in threads:
    t.start()
for t in threads:
    t.join()
print(results)

0

Kindall cevabı Python3 içinde

class ThreadWithReturnValue(Thread):
    def __init__(self, group=None, target=None, name=None,
                 args=(), kwargs={}, *, daemon=None):
        Thread.__init__(self, group, target, name, args, kwargs, daemon)
        self._return = None 

    def run(self):
        try:
            if self._target:
                self._return = self._target(*self._args, **self._kwargs)
        finally:
            del self._target, self._args, self._kwargs 

    def join(self,timeout=None):
        Thread.join(self,timeout)
        return self._return

-2

Bir işlevin çağrısından yalnızca Doğru veya Yanlış doğrulanacaksa, bulduğum daha basit bir çözüm genel listeyi güncelleştiriyor.

import threading

lists = {"A":"True", "B":"True"}

def myfunc(name: str, mylist):
    for i in mylist:
        if i == 31:
            lists[name] = "False"
            return False
        else:
            print("name {} : {}".format(name, i))

t1 = threading.Thread(target=myfunc, args=("A", [1, 2, 3, 4, 5, 6], ))
t2 = threading.Thread(target=myfunc, args=("B", [11, 21, 31, 41, 51, 61], ))
t1.start()
t2.start()
t1.join()
t2.join()

for value in lists.values():
    if value == False:
        # Something is suspicious 
        # Take necessary action 

Bu, iş parçacıklarından herhangi birinin gerekli eylemi gerçekleştirmek için yanlış bir durum döndürüp döndürmediğini bulmak istediğinizde daha yararlıdır.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.