Programing

비단뱀의 스레드에서 반환 값을 가져오는 방법

c10106 2022. 3. 7. 21:15
반응형

비단뱀의 스레드에서 반환 값을 가져오는 방법

그 은 할 수 없다.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구현... 가 있어하지만 그것은 효과가 있다.

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

.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.

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)

제이크의 대답은 좋지만, 실풀을 사용하고 싶지 않다면(몇 개의 실이 필요할지는 모르지만 필요에 따라 만들면 된다) 실 사이에 정보를 전달하는 좋은 방법은 내장된 큐(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

장식된 함수는 호출될 때마다 새 스레드를 생성하고 결과를 수신할 대기열이 들어 있는 스레드 객체를 반환한다.

갱신하다

내가 이 답을 올린 지 꽤 오래되었지만, 여전히 조회수가 올라오기 때문에, 나는 파이썬의 새로운 버전에서 이것을 하는 방법을 반영하기 위해 그것을 업데이트 해야겠다고 생각했다.

Python 3.2는 병렬 작업을 위한 높은 수준의 인터페이스를 제공하는 모듈에 추가되었다.그것은 제공한다.ThreadPoolExecutor그리고ProcessPoolExecutor같은 api로 스레드 또는 프로세스 풀을 사용할 수 있다.

한 이점은 의 한국 가구에 하는 이다.Executor개체를 반환하고, 이 값은 사용자가 제출한 호출 가능의 반환 값으로 완료된다.

이렇게 하면 A를 부착할 수 있다.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 2.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

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

내가 찾아낸 대부분의 답은 길고 다른 모듈이나 고급 비단뱀 기능에 익숙해져 있어야 하며, 그 답이 말하는 모든 것에 이미 익숙하지 않다면 오히려 누군가에게 혼란을 줄 것이다.

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

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__우리가 하고 그 속성( property)으로self.result스레드의 실행이 완료된 후

이것의 인스턴스를 생성하는 것은 의 인스턴스를 생성하는 것과 동일하다.threading.Thread. 새 스레드에서 실행할 함수를 에 전달target당신의 기능이 필요로 하는 논쟁과 모든 논쟁은argskwargs언쟁점

예)

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

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

질문을 받고 이 루동에 대답하고 있다는 것을 알지만, 앞으로 더 많은 사람들에게 도움이 되었으면 좋겠어!


편집: 위와 같은 코드에 액세스하여 프로젝트 전체에 걸쳐 재사용할 수 있도록 PyPI 패키지를 작성했다(GitHub 코드가 여기에 있음).PyPI 패키지는 완전히 확장되며threading.Thread class에서 할 수 , class는 class에서 을 수 있다.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)

나는 킨들의 대답을 훔쳐서 조금 치웠다.

시간 초과를 처리하기 위해 *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 둘 다에서 실행될 코드로 업데이트하기로 결정했다.

또한, 나는 이 질문에 대한 많은 해답들이 스레드.조인()에 대한 이해 부족을 보여준다.몇몇은 완전히 그 일을 처리하지 못한다.timeout (1arg를 할 수 . 그러나 (1)을 반환할 수 있는 대상 함수가 있는 경우에 대해 알아야 하는 코너 케이스도 있다.None그리고 (2) 당신은 또한timeout가가하위해 arg()이 모서리 케이스를 이해하려면 "테스트 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()가 none을 반환할 것으로 예상(테스트 2 참조)하지만, 시간이 초과되면 none도 반환할 것으로 예상한다는 점이다.

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

(1) 그것이 바로 giveMe()를 돌려준 것이다.

(2) 조인 시간 초과

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

나는 이 포장지를 사용하고 있는데, 이 포장지는 어떤 기능도 편안하게 달리게 한다.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모듈

나사산 기능의 편안한 반환 값과 예외 처리 기능은 "피소닉"의 빈번한 필요사항이며, 실제로 이미 본사가 제공해야 한다.threading모듈 - 표준에서 직접 사용 가능Thread계급ThreadPool단순한 업무에는 너무 많은 오버헤드가 있다. 3개의 스레드 관리, 많은 관료주의.도 불도도Thread의 레이아웃은 원래 Java에서 복사되었으며, 예를 들어 여전히 쓸모 없는 1번째(!) 생성자 매개 변수에서 복사됨group.

언급된 kindall을 기반으로 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

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

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'}

결과의 키로 기능 입력을 사용하는 경우, 모든 고유한 입력이 결과에 입력되도록 보장된다.

을 하십시오.
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수정하지 않고 이 기본 계획을 따르십시오.

GuySoft의 아이디어는 훌륭하지만, 나는 그 개체가 반드시 스레드로부터 물려받을 필요는 없고, 시작()이 인터페이스에서 제거될 수 있다고 생각한다.

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)

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

한 가지 일반적인 해결책은 당신의 기능을 포장하는 것이다.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, 임의의 스레드-안전한 데이터 구조를 선택할 수 있으므로 )

파이톤3의 킨들

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

참조URL: https://stackoverflow.com/questions/6893968/how-to-get-the-return-value-from-a-thread-in-python

반응형