Pythonのスレッドから戻り値を取得するにはどうすればよいですか?


342

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

回答:


37

Python 3.2以降では、stdlib concurrent.futuresモジュールがに高レベルのAPIを提供します。これには、threading戻り値や例外をワーカースレッドからメインスレッドに渡すことも含まれます。

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)

不思議に思う人のために、これはスレッドのリストで行うことができます。futures = [executor.submit(foo, param) for param in param_list]順序は維持され、終了するwithと結果の収集が可能になります。[f.result() for f in futures]
jayreed1

273

FWIW、multiprocessingモジュールには、Poolクラスを使用してこれを行うための優れたインターフェースがあります。また、プロセスではなくスレッドを使いたい場合は、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.

50
@JakeBiesinger私のポイントは、私が答えを探していたこと、スレッドからの応答を得る方法、ここに来た、そして受け入れられた答えは述べられた質問に答えないことです。スレッドとプロセスを区別します。グローバルインタープリターロックについて知っていますが、I / Oバウンドの問題に取り組んでいるため、スレッドは問題なく、プロセスは必要ありません。他の答えはここで述べたより良い答えの質問です。
omikron、2015

7
@omikronしかし、Pythonのスレッドは、この機能を有効にするサブクラスを使用しない限り、応答を返しません。可能なサブクラスのうち、ThreadPoolsが最適です(スレッド数を選択し、map / apply w / sync / asyncを使用してください)。からインポートされmultiprocessますが、プロセスとは関係ありません。
Jake Biesinger、2015

4
@JakeBiesingerああ、私は盲目です。私の不必要なコメントでごめんなさい。あなたが正しいです。multiprocessing = processesだと思いました。
omikron

12
processes=1より多くのスレッドがある場合は、複数に設定することを忘れないでください!
イマン

4
マルチプロセッシングとスレッドプールの問題は、基本的なスレッドライブラリと比較して、セットアップとスレッドの開始がはるかに遅いことです。実行時間の長いスレッドを開始するのに最適ですが、実行時間の短いスレッドを大量に開始する必要がある場合は、目的を達成できません。ここで他の回答に記載されている「スレッド化」と「キュー」を使用するソリューションは、私の意見では後者のユースケースのより良い代替案です。
Yves Dorfsman

242

私が見た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実装に固有の「プライベート」データ構造にアクセスしますが、機能します。

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

37
クール、例をありがとう!そもそもなぜThreadが戻り値を処理するように実装されなかったのか、それはサポートするのに十分なことのように思われます。
WIM

16
私はこれが受け入れられる答えになるはずだと思います-OPはthreading別のライブラリを試すのではなく、を要求しました。さらに、プールサイズの制限により、私の場合に発生した追加の潜在的な問題が発生します。
domoarigato 2015年

10
素晴らしい電車のジョーク。
meawoppl 2015年

7
python3ではこれは戻ります TypeError: __init__() takes from 1 to 6 positional arguments but 7 were given。それを修正する方法はありますか?
GuySoft 2016年

2
これらの2番目(_Thread__target事)を実行したい人のための警告。コードをpython 3に移植しようとする人は誰でも、あなたがやったことを解決するまで嫌いになります(2と3の間で変更された文書化されていない機能を使用しているため)。コードを文書化します。
ベンテイラー

84

ジェイクの答えは良いですが、スレッドプールを使用したくない場合(必要なスレッドの数は不明ですが、必要に応じて作成します)、スレッド間で情報を送信するための良い方法は組み込みです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オブジェクトを返します。

更新

この回答を投稿してからしばらく経ちますが、それでもビューが表示されるため、新しいバージョンのPythonでこれを行う方法を反映するように更新するつもりです。

Python 3.2が concurrent.futures並列タスクに高レベルのインターフェースを提供するモジュールに。とを提供ThreadPoolExecutorしているProcessPoolExecutorため、同じAPIでスレッドまたはプロセスプールを使用できます。

このAPIの1つの利点は、タスクを ExecutorFutureオブジェクトが返され、送信した呼び出し可能オブジェクトの戻り値でオブジェクトが完了することです。

これにより、 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.Futurewith に変換できること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()

これを機能させることができないようです。AttributeError: 'module' object has no attribute 'Lock'これは行から発せられているように見えるというエラーが表示されますy = long_task(10)...考え?
sadmicrowave 2013

1
コードはLockを明示的に使用しないため、問題はコードの他の場所にある可能性があります。あなたはそれについての新しいSO質問を投稿したいかもしれません
bj0

なぜresult_queueがインスタンス属性なのですか?明示的で曖昧ではない@threadedを使用するときに、ユーザーがresult_queueを呼び出す必要がないように、それがクラス属性である場合は、より良いでしょうか?
ノンボット2017

@ t88、意味がわからない、結果にアクセスする方法が必要です。つまり、何を呼び出すかを知る必要があります。それを別のものにしたい場合は、スレッドをサブクラス化して、必要なことを実行できます(これは簡単な解決策でした)。キューをスレッドにアタッチする必要があるのは、複数の呼び出し/関数が独自のキューを持つためです
bj0

1
これは素晴らしいです!どうもありがとうございました。
ガネーシュカティレサン2018

53

既存のコードを変更する必要がない別のソリューション:

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

t = Thread(target = lambda q、arg1:q.put(foo(arg1))、args =(que、 'world!'))q.putがここで行うこと、Queue.Queue()が行うこと
vijay shanker 2016年

6
出身地にあなたの像があるはずです、ありがとう!
Onilol

3
@Onilol-ありがとうございました。あなたのコメントがまさに私がこれをする理由です:)
Arik

4
Python3の場合、に変更する必要がありfrom queue import Queueます。
Gino Mempin

1
これは、戻り値がメインスレッドに戻ることができるようにするための最も混乱の少ない方法(元のコードベースを大幅に再構築する必要がない)のようです。
Fanchen Bao

24

Parris / kindallの回答 join / returnPython 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に異なる方法で実装されています。


1
joinは、渡されるタイムアウトパラメータを取ります
cz

22

私はkindallの答えを盗んで、ほんの少しだけ片付けました。

重要な部分は、タイムアウトを処理するために* argsと** kwargsをjoin()に追加することです

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()に関する理解の欠如を示すこの質問に対する多くの回答が表示されます。timeoutarg を完全に処理できないものもあります。ただし、(1)返すことができるターゲット関数がNoneあり、(2)また、timeout引数をjoin()に。このコーナーケースを理解するには、「テスト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()

Python3に相当する_Thread_targetを知っていますか?その属性はPython3には存在しません。
GreySage 2016年

threading.pyファイルを調べたところ、_targetであることがわかりました(他の属性も同様に名前が付けられています)。
GreySage 2016年

in 、、、およびの引数をクラスのメンバー変数として保存するtargetargs、スレッドクラスのプライベート変数へのアクセスを回避できます。kwargs
Tolli

@GreySage私の答えをご覧ください。このブロックを下のpython3に移植しました
GuySoft

@GreySageの回答でpy2とpy3がサポートされるようになりました
user2426679 2018

15

キューの使用:

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
本当にこの溶液のように、短くて甘い。関数が入力キューを読み取り、に追加するout_queue1場合は、ループしout_queue1.get()てQueue.Empty例外をキャッチする必要がありますret = [] ; try: ; while True; ret.append(out_queue1.get(block=False)) ; except Queue.Empty: ; pass。改行をシミュレートするセミコロン。
sastorsl 2018

6

問題に対する私の解決策は、関数とスレッドをクラスでラップすることです。プール、キュー、または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()

例外をどのように処理しますか?add関数が指定され、intとstrがあるとします。すべてのスレッドが失敗しますか、それとも1つだけが失敗しますか?
user1745713

4

join常にreturn NoneですThread。戻りコードを処理するには、サブクラス化する必要があると思います。


4

@JakeBiesinger回答に対する@imanコメントを考慮して、さまざまな数のスレッドを持つように再構成しました。

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

乾杯、

男。


2

スレッド化された関数のスコープの上にミュータブルを定義し、それに結果を追加することができます。(私はまた、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'}

結果入力のキーとして関数入力を使用する場合、すべての一意の入力は結果にエントリを提供することが保証されます


2

私はこのラッパーを使用しています。このラッパー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からコピーされたものです-たとえば、まだ役に立たない1番目(!)のコンストラクター・パラメーターからそれを見ることができますgroup


最初のコンストラクタは役に立たず、将来の実装のために予約されています
。Python

1

ターゲットを
1)に定義するq
2)引数を取る2)ステートメントreturn fooq.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を変更せずに使用できるようにすることもできますが、この基本的なスキームに従ってください。


それはする必要がありますresults = [ans_q.get() for _ in xrange(len(threads))]
Hemant H Kumar

1

前述のように、マルチプロセッシングプールは基本的なスレッド化よりもはるかに低速です。ここでいくつかの回答で提案されているキューを使用することは、非常に効果的な代替手段です。多くの小さなスレッドを実行し、それらを辞書と組み合わせることで複数の回答を回復できるようにするために、それを辞書で使用しました。

#!/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のアイデアは素晴らしいですが、オブジェクトは必ずしも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

0

通常の解決策の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


0

なぜグローバル変数を使用しないのですか?

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

Python3におけるKindallの回答

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

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 

これは、スレッドのいずれかが必要なアクションを実行するためにfalseステータスを返したかどうかを確認したい場合により役立ちます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.