以下の関数foo
は文字列'foo'
を返します。スレッドのターゲットから返される値'foo'
を取得するにはどうすればよいですか?
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()
上記の "1つの明白な方法"はうまくいきません:thread.join()
はNone
を返しました。
FWIW、multiprocessing
モジュールはPool
クラスを使ったこれに対するNiceインタフェースを持っています。プロセスではなくスレッドを使いたい場合は、ドロップインの代わりにmultiprocessing.pool.ThreadPool
クラスを使用できます。
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.
私が見た1つの方法は、リストや辞書のような可変オブジェクトをスレッドのコンストラクタに渡し、インデックスやその他の識別子を付け加えることです。スレッドはその結果をそのオブジェクトの専用スロットに格納できます。例えば:
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()
が呼び出された関数の戻り値を本当に返したい場合は、次のようなThread
サブクラスを使用してこれを実行できます。
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
これは名前マングルのせいで少々毛深いものになります、そしてそれはThread
の実装に特有の "private"データ構造にアクセスします...しかしそれはうまくいきます。
Python3の場合
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
Jakeの答えはいいのですが、スレッドプールを使いたくない場合(必要なスレッドの数がわからないが、必要に応じてそれらを作成します)、スレッド間で情報をやり取りするための良い方法は組み込みです。 Queue.Queue クラス、スレッドセーフを提供します。
スレッドプールと同じように動作するように、次のデコレータを作成しました。
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
それからあなたはそれを次のように使うだけです:
@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
装飾関数は、呼び出されるたびに新しいスレッドを作成し、結果を受け取るキューを含むThreadオブジェクトを返します。
UPDATE
この回答を投稿してからかなり時間がかかりますが、それでも意見は得られるので、新しいバージョンのPythonでこれを行う方法を反映するように更新することにしました。
Python 3.2は concurrent.futures
モジュールに追加されました。これは並列タスクのためのハイレベルなインターフェースを提供します。これはThreadPoolExecutor
とProcessPoolExecutor
を提供するので、同じapiを持つスレッドまたはプロセスプールを使用できます。
このAPIの利点の1つは、タスクをExecutor
に送信すると、 Future
オブジェクトが返されることです。これは、送信した呼び出し可能オブジェクトの戻り値で完了します。
これにより、queue
オブジェクトを添付する必要がなくなり、デコレータがかなり単純化されます。
_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
渡されない場合、これはデフォルトモジュールスレッドプールエグゼキュータを使用します。
使い方は以前とよく似ています。
@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+を使用している場合、このメソッド(およびFutureオブジェクト全般)を使用することの本当に素晴らしい機能の1つは、返されたfutureをラップして asyncio.Future
に変換できることです。 asyncio.wrap_future
。これにより、コルーチンと簡単に連携できます。
result = await asyncio.wrap_future(long_task(10))
基礎となるconcurrent.Future
オブジェクトにアクセスする必要がない場合は、デコレータにラップを含めることができます。
_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
それで、あなたがイベントループスレッドからCPU集中的またはブロックコードをプッシュする必要があるときはいつでも、あなたはそれを装飾された関数に入れることができます:
@threadpool
def some_long_calculation():
...
# this will suspend while the function is executed on a threadpool
result = await some_long_calculation()
既存のコードを変更する必要がないもう1つの解決策:
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
マルチスレッド環境にも簡単に調整できます。
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
Parris/kindallの answerjoin
/return
answerはPython 3に移植されました。
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
Thread
クラスはPython 3では異なる方法で実装されています。
私はkindallの答えを盗み、ほんの少しそれを片付けました。
重要なのは、タイムアウトを処理するためにjoin()に* argsと** kwargsを追加することです。
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
回答が下に更新されました
これは私の最も一般的に支持されている答えなので、私はpy2とpy3の両方で動作するコードで更新することにしました。
さらに、Thread.join()に関する理解が不足していることを示す、この質問に対する多くの回答があります。 timeout
引数を完全に処理できない人もいます。しかし、(1)None
を返すことができるターゲット関数がある場合、および(2)join()にtimeout
引数を渡す場合も、インスタンスに関して注意する必要があります。このコーナーケースを理解するには、「テスト4」を参照してください。
Py2とpy3で動作するThreadWithReturnクラス:
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
いくつかのサンプルテストを以下に示します。
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で遭遇する可能性がある稀なケースを特定できますか?
問題は、giveMe()がNoneを返すことを期待することです(テスト2を参照)が、タイムアウトした場合はjoin()がNoneを返すことも期待します。
returned is None
は次のいずれかを意味します。
(1)giveMe()が返したもの、または
(2)join()がタイムアウトしました
GiveMe()は常にNoneを返すことがわかっているので、この例は簡単です。しかし、現実のインスタンス(ターゲットが正当にNoneまたはその他のものを返す可能性がある場合)では、何が起こったのかを明示的に確認したいと思います。
このコーナーケースに対処する方法は次のとおりです。
# 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()
キューを使用する
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())
この問題に対する私の解決策は、関数とスレッドをクラスにラップすることです。プール、キュー、またはc型の変数渡しを使用する必要はありません。それはまた非ブロッキングです。代わりに状況を確認してください。コードの終わりにそれを使用する方法の例を参照してください。
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()
考慮に入れる@ iman@ JakeBiesinger答え私はそれを様々な数のスレッドを持つように再構成しました:
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()
乾杯、
男。
スレッド化された関数の範囲を超えてミュータブルを定義し、それに結果を追加することができます。 (私はまたpython3互換性があるようにコードを修正しました)
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)
これは{'world!': 'foo'}
を返します
あなたがあなたの結果の辞書へのキーとして関数入力を使うならば、あらゆるユニークな入力は結果にエントリーを与えることが保証されています
以下のように、Poolをワーカープロセスのプールとして使用できます。
from multiprocessing import Pool
def f1(x, y):
return x*y
if __== '__main__':
with Pool(processes=10) as pool:
result = pool.apply(f1, (2, 3))
print(result)
join
は常にNone
を返します。戻りコードなどを処理するにはThread
をサブクラス化する必要があると思います。
私はこのラッパーを使用しています。これはThread
内で実行するための関数を快適に変えます - その戻り値や例外に注意します。 Queue
のオーバーヘッドは増えません。
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
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.")
threading
モジュールに関する注意スレッド関数の快適な戻り値と例外処理は頻繁に "Pythonic"に必要とされており、実際には既にthreading
モジュールによって - おそらく標準のThread
クラスに直接提供されるべきです。 ThreadPool
は単純なタスクには多すぎるオーバーヘッドを抱えています - 3スレッドの管理、多くの官僚主義。残念ながらThread
のレイアウトはもともとJavaからコピーされたものです - まだ役に立たない1st(!)コンストラクタパラメータgroup
から。
GuySoftのアイデアは素晴らしいですが、オブジェクトは必ずしもThreadから継承する必要はなく、start()はインターフェイスから削除される可能性があります。
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
前述したように、マルチプロセッシングプールは基本的なスレッディングよりはるかに遅いです。ここでいくつかの答えで提案されているようにキューを使用することは非常に効果的な代替手段です。たくさんの小さなスレッドを実行し、それらを辞書と組み合わせることで複数の答えを取り戻すことができるようにするために、辞書でそれを使用しました。
#!/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つは、関数foo
を次のようなデコレータでラップすることです。
result = queue.Queue()
def task_wrapper(*args):
result.put(target(*args))
それから全体のコードはそのように見えるかもしれません
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
1つの重要な問題は、戻り値が順序付けされていないということです。 (実際、任意のスレッドセーフなデータ構造を選択できるので、return value
は必ずしもqueue
に保存されるわけではありません)
ターゲットをに定義します
1)引数q
を取ります
2)ステートメントreturn foo
をq.put(foo); return
で置き換えます
だから機能
def func(a):
ans = a * a
return ans
になる
def func(a, q):
ans = a * a
q.put(ans)
return
それからあなたはそのように進むでしょう
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))]
そして、関数デコレータ/ラッパーを使ってそれを作ることができるので、既存の関数を修正せずにtarget
として使うことができますが、この基本的な方法に従います。
なぜグローバル変数を使わないのですか?
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)
TrueまたはFalseのみが関数の呼び出しから検証される場合、私が見つけたもっと簡単な解決策はグローバルリストを更新することです。
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
これは、必要な処置をとるためにいずれかのスレッドが偽の状況を戻したかどうかを調べたい場合に、より役立ちます。
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
私のようなダミーのためにこれを成し遂げるための非常に簡単な方法:
import queue
import threading
# creating queue instance
q = queue.Queue()
# creating threading class
class AnyThread():
def __init__ (self):
threading.Thread.__init__(self)
def run(self):
# in this class and function we will put our test target function
test()
t = AnyThread()
# having our test target function
def test():
# do something in this function:
result = 3 + 2
# and put result to a queue instance
q.put(result)
for i in range(3): #calling our threading fucntion 3 times (just for example)
t.run()
output = q.get() # here we get output from queue instance
print(output)
>>> 5
>>> 5
>>> 5
ここでの主なもの - queue
モジュールです。 queue.Queue()
インスタンスを作成し、それを私たちの関数に含めます。結果をフィードして、後でスレッドを超えます。
テスト関数に引数を渡したもう1つの例を見てください。
import queue
import threading
# creating queue instance
q = queue.Queue()
# creating threading class
class AnyThread():
def __init__ (self):
threading.Thread.__init__(self)
def run(self, a, b):
# in this class and function we will put our execution test function
test(a, b)
t = AnyThread()
# having our test target function
def test(a, b):
# do something in this function:
result = a + b
# and put result to a queue instance
q.put(result)
for i in range(3): #calling our threading fucntion 3 times (just for example)
t.run(3+i, 2+i)
output = q.get() # here we get output from queue instance
print(output)
>>> 5
>>> 7
>>> 9