programing

스레드에서 반환 값을 가져오는 방법은 무엇입니까?

easyjava 2023. 5. 9. 23:14
반응형

스레드에서 반환 값을 가져오는 방법은 무엇입니까?

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

위에 표시된 "분명한 한 가지 방법"은 작동하지 않습니다.thread.join()된 환된반None.

제가 본 한 가지 방법은 목록이나 사전과 같은 가변 객체를 인덱스나 다른 종류의 식별자와 함께 스레드의 생성자에게 전달하는 것입니다.그런 다음 스레드는 해당 개체의 전용 슬롯에 결과를 저장할 수 있습니다.예:

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하지만 하지만 그것은 효과가 있다.

Python 3의 경우:

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

, FWIW, »multiprocessing은 이은위멋인가있를지습다니고스이터모듈페를 하여 이를 를 가지고 .Poolclass. 클스를 . 그리고 만약 당신이 프로세스보다 스레드를 고수하고 싶다면, 당신은 그냥 사용할 수 있습니다.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.

Python 3.2+에서 stdlib 모듈은 더 높은 수준의 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)

Jake의 대답은 좋지만 스레드 풀(필요한 스레드 수는 모르지만 필요에 따라 생성)을 사용하지 않으려면 기본 제공 큐를 사용하여 스레드 간에 정보를 전송하는 것이 좋습니다.스레드 안전을 제공하는 큐 클래스입니다.

스레드 풀과 유사한 방식으로 작동하도록 하기 위해 다음과 같은 장식기를 만들었습니다.

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

장식된 함수는 호출될 때마다 새 스레드를 만들고 결과를 수신할 대기열이 포함된 스레드 개체를 반환합니다.

갱신하다

이 답변을 게시한 지 꽤 오래되었지만 여전히 조회 수가 있으므로 최신 버전의 Python에서 수행하는 방식을 반영하여 업데이트하려고 생각했습니다.

모듈에 병렬 작업을 위한 고급 인터페이스를 제공하는 Python 3.2가 추가되었습니다.제공합니다.ThreadPoolExecutor그리고.ProcessPoolExecutor동일한 api로 스레드 또는 프로세스 풀을 사용할 수 있습니다.

한은 api에 입니다.Executor개체를 반환합니다. 이 개체는 사용자가 제출한 호출부호의 반환 값으로 완료됩니다.

이를은붙합니를 붙이게 .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 객체)을 사용할 때의 한 가지 좋은 기능은 반환된 미래를 와 함께 변환할 수 있다는 것입니다.이를 통해 코루틴에서 쉽게 작동할 수 있습니다.

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

기존 코드를 변경할 필요가 없는 또 다른 솔루션:

import Queue             # Python 2.x
#from queue import Queue # Python 3.x

from threading import Thread

def foo(bar):
    print 'hello {0}'.format(bar)     # Python 2.x
    #print('hello {0}'.format(bar))   # Python 3.x
    return 'foo'

que = Queue.Queue()      # Python 2.x
#que = Queue()           # Python 3.x

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

또한 멀티 스레드 환경에 쉽게 조정할 수 있습니다.

import Queue             # Python 2.x
#from queue import Queue # Python 3.x
from threading import Thread

def foo(bar):
    print 'hello {0}'.format(bar)     # Python 2.x
    #print('hello {0}'.format(bar))   # Python 3.x
    return 'foo'

que = Queue.Queue()      # Python 2.x
#que = Queue()           # Python 3.x

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         # Python 2.x
    #print(result)       # Python 3.x

업데이트:

스레드의 결과를 저장할 수 있는 훨씬 간단하고 간결한 방법이 있으며, 인터페이스를 거의 동일하게 유지할 수 있는 방법이 있다고 생각합니다.threading.Threadclass -를 하지 않았습니다): class (class):

import threading

class ConciseResult(threading.Thread):
    def run(self):
        self.result = self._target(*self._args, **self._kwargs)

강력하고 잠재적인 오류를 방지하기 위해:

import threading

class ConciseRobustResult(threading.Thread):
    def run(self):
        try:
            if self._target is not None:
                self.result = self._target(*self._args, **self._kwargs)
        finally:
            # Avoid a refcycle if the thread is running a function with
            # an argument that has a member that points to the thread.
            del self._target, self._args, self._kwargs

간략한 설명: 우리는 단지 그것을 무시합니다.runthreading.Thread다른 것은 수정하지 않습니다.이를 통해 다른 모든 것을 사용할 수 있습니다.threading.Thread클래스는 원래 게시물에서 수행하는 방식으로 속성 할당이나 사용자 지정 속성 수정과 같은 잠재적인 에지 사례를 누락할 염려 없이 우리에게 도움이 됩니다.

우리는 우리가 단지 수정하는 것을 확인할 수 있습니다.run의 을 보는 help(ConciseResult)그리고.help(ConciseRobustResult)아에포속유메설일서함한/된드성/명자래설에 포함된 한 메서드Methods defined here:이라run그리고 다른 모든 것들은 유전된 것들로부터 옵니다.threading.Thread 클래스)Methods inherited from threading.Thread:섹션)을 클릭합니다.

아래 예제 코드를 사용하여 이러한 구현 중 하나를 테스트하려면 다음과 같이 대체합니다.ConciseResult또는ConciseRobustResult위해서ThreadWithResult에 시대에main아래의 기능.

의 폐쇄 기능을 사용한 원본 게시물init방법:

제가 찾은 대부분의 답변은 길고 다른 모듈이나 고급 파이썬 기능에 익숙해야 하며, 답변이 말하는 모든 것에 이미 익숙하지 않으면 누군가에게 다소 혼란스러울 것입니다.

단순화된 접근 방식을 위한 작업 코드:

import threading

class ThreadWithResult(threading.Thread):
    def __init__(self, group=None, target=None, name=None, args=(), kwargs={}, *, daemon=None):
        def function():
            self.result = target(*args, **kwargs)
        super().__init__(group=group, target=function, name=name, daemon=daemon)

코드 예제:

import time, random


def function_to_thread(n):
    count = 0
    while count < 3:
            print(f'still running thread {n}')
            count +=1
            time.sleep(3)
    result = random.random()
    print(f'Return value of thread {n} should be: {result}')
    return result


def main():
    thread1 = ThreadWithResult(target=function_to_thread, args=(1,))
    thread2 = ThreadWithResult(target=function_to_thread, args=(2,))
    thread1.start()
    thread2.start()
    thread1.join()
    thread2.join()
    print(thread1.result)
    print(thread2.result)

main()

설명:저는 작업을 크게 단순화하고 싶었기 때문에,ThreadWithResult.threading.Thread 함수 포내 수function__init__값을 저장할 스레드 함수를 호출하고 중첩 함수의 결과를 인스턴스 속성으로 저장합니다.self.result스레드 실행이 완료된 후.

를 만드는 은 이인턴를만것다은인동것다만니의 것과 .threading.Thread를 새스드에함다전음달다니합로에 합니다.target및할 수 를 사용합니다.args에 대한 인수 및 kwargs논쟁.

예.

my_thread = ThreadWithResult(target=my_function, args=(arg1, arg2, arg3))

저는 이것이 대다수의 대답보다 훨씬 이해하기 쉬우며, 이 접근법은 추가적인 수입이 필요하지 않다고 생각합니다!나는 포함시켰습니다.time그리고.random모듈을 사용하여 스레드의 동작을 시뮬레이션하지만 원래 질문에서 질문한 기능을 달성할 필요는 없습니다.

질문이 있은 지 한참 후에 제가 이 바보 같은 소리에 대답하고 있다는 것을 알지만, 이것이 앞으로 더 많은 사람들에게 도움이 될 수 있기를 바랍니다!


편집: 위의 동일한 코드에 액세스하여 프로젝트 간에 재사용할 수 있도록 PyPI 패키지를 만들었습니다(GitHub 코드는 여기에 있습니다).PyPI 패키지는 다음을 완전히 확장합니다.threading.Thread사용하여 래스클에서 할 수 . 설정할 속성을 설정할 수 있습니다.threading.thread에서.ThreadWithResult수업도!

위의 원래 답변은 이 하위 클래스 뒤에 있는 주요 아이디어에 대해 설명하지만, 자세한 내용은 여기에서 (모듈 문서 문자열에서) 더 자세한 설명을 참조하십시오.

빠른 사용 예:

pip3 install -U save-thread-result     # MacOS/Linux
pip  install -U save-thread-result     # Windows

python3     # MacOS/Linux
python      # Windows
from save_thread_result import ThreadWithResult

# As of Release 0.0.3, you can also specify values for
#`group`, `name`, and `daemon` if you want to set those
# values manually.
thread = ThreadWithResult(
    target = my_function,
    args   = (my_function_arg1, my_function_arg2, ...)
    kwargs = {my_function_kwarg1: kwarg1_value, my_function_kwarg2: kwarg2_value, ...}
)

thread.start()
thread.join()
if getattr(thread, 'result', None):
    print(thread.result)
else:
    # thread.result attribute not set - something caused
    # the thread to terminate BEFORE the thread finished
    # executing the function passed in through the
    # `target` argument
    print('ERROR! Something went wrong while executing this thread, and the function you passed in did NOT complete!!')

# seeing help about the class and information about the threading.Thread super class methods and attributes available:
help(ThreadWithResult)

패리스 / 킨들의 대답 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에서 다르게 구현됩니다.

저는 킨들의 답변을 훔쳐서 조금 치웠습니다.

주요 부분은 제한 시간을 처리하기 위해 *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()에 대한 이해가 부족한 답변이 많이 보입니다.일부는 완전히 처리하지 못합니다.timeout ( 할 수 있는 대상 가 있는 경우 해야 할 . 그러나 (1) 반환할 수 있는 대상 함수가 있는 경우 인스턴스와 관련하여 주의해야 할 코너 케이스도 있습니다.None그리고 (2) 당신은 또한 통과합니다.timeout케이스를 하려면 " 4하십시오.이 코너 사례를 이해하려면 "테스트 4"를 참조하십시오.

py2 및 py3에서 작동하는 ThreadWithReturn 클래스:

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

_thread_target_key, _thread_args_key, _thread_kwargs_key = (
    ('_target', '_args', '_kwargs')
    if sys.version_info >= (3, 0) else
    ('_Thread__target', '_Thread__args', '_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 target is not 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))

테스트 4에서 발생할 수 있는 코너 케이스를 식별할 수 있습니까?

문제는 giveMe()가 없음을 반환할 것으로 예상하지만(테스트 2 참조), 시간이 초과되면 join()이 없음을 반환할 것으로 예상한다는 것입니다.

returned is None다음 중 하나를 의미합니다.

그게 바로 내게 돌려준 것입니다.

join(가입) 시간 초과

giveMe()가 항상 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()

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

저는 이 포장지를 사용하고 있는데, 이 포장지는 어떤 기능이든 편안하게 작동합니다.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 컨트롤 모듈

값 및 한 "이며, 사기파편반빈값처및예리번 "는토닉이며", ▁the▁is▁▁by▁be▁handling▁indeed▁offered▁already▁ofpy▁value"다합니▁function▁and▁&▁should▁exception▁need에서 제공되어야 합니다.threading모듈 - 표준에 직접 포함될 수 있습니다.Thread학급.ThreadPool단순한 작업에는 너무 많은 오버헤드가 있습니다. 3가지 스레드 관리, 많은 관료주의.도 불하게도행.Thread원래 를 들어 첫 번째 매개변수에서 되었습니다. 보시는 바와 같이 여전히 쓸모없는 첫 번째(!) 생성자 매개 변수에서 복사되었습니다.group.

언급된 모든 종류의 솔루션을 기반으로 Python3에서 작동하는 보다 일반적인 솔루션을 소개합니다.

import threading

class ThreadWithReturnValue(threading.Thread):
    def __init__(self, *init_args, **init_kwargs):
        threading.Thread.__init__(self, *init_args, **init_kwargs)
        self._return = None
    def run(self):
        self._return = self._target(*self._args, **self._kwargs)
    def join(self):
        threading.Thread.join(self)
        return self._return

사용.

        th = ThreadWithReturnValue(target=requests.get, args=('http://www.google.com',))
        th.start()
        response = th.join()
        response.status_code  # => 200

join 되돌아가다None저는 당신이 서브클래스를 해야 한다고 생각합니다.Thread반송 코드 등을 처리할 수 있습니다.

스레드 함수의 범위 위에 변수를 정의하고 결과를 추가할 수 있습니다. (또한 코드를 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'}

함수 입력을 결과 딕트의 키로 사용하면 모든 고유 입력이 결과에 입력됩니다.

제가 발견한 가장 짧고 간단한 방법은 Python 클래스와 동적 속성을 활용하는 것입니다.에서 음을사용생스컨내현에텍재스있검수를 사용하여 할 수 .threading.current_thread()속성에 반환 값을 할당합니다.

import threading

def some_target_function():
    # Your code here.
    threading.current_thread().return_value = "Some return value."

your_thread = threading.Thread(target=some_target_function)
your_thread.start()
your_thread.join()

return_value = your_thread.return_value
print(return_value)

당신의 를 표를정니목다로하세요.
q
문을 합니다.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수정하지 않고 이 기본 계획을 따릅니다.

GuySoft의 아이디어는 훌륭하지만, 객체가 스레드에서 상속될 필요는 없고 인터페이스에서 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

이것은 꽤 오래된 질문이지만, 저에게 효과가 있고 개발 프로세스에 도움이 되는 간단한 솔루션을 공유하고 싶었습니다.

함수인 "새로운" 대상 함수, "새로운" 대상 함수,inner원래 함수의 결과를 할당하는 것입니다(을 통해 전달됨).__init__ to 능에한대기▁result폐쇄라는 것을 통한 래퍼의 인스턴스 속성입니다.

이를 통해 래퍼 클래스는 호출자가 언제든지 액세스할 수 있는 반환 값을 보유할 수 있습니다.

참고: 이 메서드는 다음과 같은 임의의 메서드나 개인 메서드를 사용할 필요가 없습니다.threading.Thread항복 함수가 고려되지 않았지만 등급.

맛있게 드세요!

from threading import Thread as _Thread


class ThreadWrapper:
    def __init__(self, target, *args, **kwargs):
        self.result = None
        self._target = self._build_threaded_fn(target)
        self.thread = _Thread(
            target=self._target,
            *args,
            **kwargs
        )

    def _build_threaded_fn(self, func):
        def inner(*args, **kwargs):
            self.result = func(*args, **kwargs)
        return inner

또실행수있다니를 도 있습니다.pytest되어 있는 ) 를 보여줍니다

import time
from commons import ThreadWrapper


def test():

    def target():
        time.sleep(1)
        return 'Hello'

    wrapper = ThreadWrapper(target=target)
    wrapper.thread.start()

    r = wrapper.result
    assert r is None

    time.sleep(2)

    r = wrapper.result
    assert r == 'Hello'

언급했듯이 멀티프로세싱 풀은 기본 스레드보다 훨씬 느립니다.일부 답변에서 제안한 대로 대기열을 사용하는 것은 매우 효과적인 대안입니다.사전과 함께 사용하여 많은 작은 스레드를 실행하고 사전과 결합하여 여러 답변을 복구할 수 있습니다.

#!/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)

여기 제가 @Kindall의 답변으로 만든 버전이 있습니다.

이 버전에서는 새 스레드를 생성하기 위해 인수와 함께 명령을 입력하기만 하면 됩니다.

이것은 Python 3.8로 만들어졌습니다.

from threading import Thread
from typing import Any

def test(plug, plug2, plug3):
    print(f"hello {plug}")
    print(f'I am the second plug : {plug2}')
    print(plug3)
    return 'I am the return Value!'

def test2(msg):
    return f'I am from the second test: {msg}'

def test3():
    print('hello world')

def NewThread(com, Returning: bool, *arguments) -> Any:
    """
    Will create a new thread for a function/command.

    :param com: Command to be Executed
    :param arguments: Arguments to be sent to Command
    :param Returning: True/False Will this command need to return anything
    """
    class NewThreadWorker(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
    
    ntw = NewThreadWorker(target = com, args = (*arguments,))
    ntw.start()
    if Returning:
        return ntw.join()

if __name__ == "__main__":
    print(NewThread(test, True, 'hi', 'test', test2('hi')))
    NewThread(test3, True)

를 사용하여 다음과 같이 값을 반환할 수 있습니다.

from multiprocessing.pool import ThreadPool

def test(num1, num2):
    return num1 + num2

pool = ThreadPool(processes=1) # Here
result = pool.apply_async(test, (2, 3)) # Here
print(result.get()) # 5

또한 다음과 같이 의 값을 반환할 수도 있습니다.

from concurrent.futures import ThreadPoolExecutor

def test(num1, num2):
    return num1 + num2

with ThreadPoolExecutor(max_workers=1) as executor:
    future = executor.submit(test, 2, 3) # Here
print(future.result()) # 5

대신 아래와 같이 배열을 사용할 수 있습니다.

from threading import Thread

def test(num1, num2, r):
    r[0] = num1 + num2 # Instead of "return"

result = [None] # Here

thread = Thread(target=test, args=(2, 3, result))
thread.start()
thread.join()
print(result[0]) # 5

대신 아래와 같이 대기열을 사용할 수도 있습니다.

from threading import Thread
import queue

def test(num1, num2, q):
    q.put(num1 + num2) # Instead of "return" 

queue = queue.Queue() # Here

thread = Thread(target=test, args=(2, 3, queue))
thread.start()
thread.join()
print(queue.get()) # '5'

한 가지 일반적인 해결책은 당신의 기능을 포장하는 것입니다.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

메모

한 가지 중요한 문제는 반환 값이 순서가 없을 수 있다는 것입니다. (사실,return value 드시저않습니다는지되에 .queue임의의 스레드 세이프 데이터 구조를 선택할 수 있으므로 )

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

이 스레드가 오래된 것은 알지만, 저도 같은 문제에 직면했습니다.사용할 의사가 있는 경우thread.join()

import threading

class test:

    def __init__(self):
        self.msg=""

    def hello(self,bar):
        print('hello {}'.format(bar))
        self.msg="foo"


    def main(self):
        thread = threading.Thread(target=self.hello, args=('world!',))
        thread.start()
        thread.join()
        print(self.msg)

g=test()
g.main()

최선의 방법은...전역 변수를 정의한 다음 스레드 함수에서 변수를 변경합니다.전달하거나 다시 검색할 항목 없음

from threading import Thread

# global var
random_global_var = 5

def function():
    global random_global_var
    random_global_var += 1

domath = Thread(target=function)
domath.start()
domath.join()
print(random_global_var)

# result: 6

언급URL : https://stackoverflow.com/questions/6893968/how-to-get-the-return-value-from-a-thread

반응형