파이썬의 스레드에서 반환 값을 얻는 방법은 무엇입니까?


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

: 위의 "그것을 할 수있는 하나의 확실한 방법은"작동하지 않습니다 thread.join()반환 None.

답변:


37

Python 3.2 이상에서 stdlib concurrent.futures모듈은 threading작업자 스레드에서 기본 스레드로 반환 값 또는 예외를 전달하는 것을 포함하여 더 높은 수준의 API를 제공합니다 .

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 내 요점은 대답을 찾고 있었고 Thread에서 응답을 얻는 방법은 여기에 왔으며 수락 된 답변은 질문에 답변하지 않았습니다. 스레드와 프로세스를 차별화합니다. Global Interpreter Lock에 대해 알고 있지만 I / O 바운드 문제를 해결하고 있으므로 스레드가 정상이므로 프로세스가 필요하지 않습니다. 다른 답변은 여기에 더 나은 답변이 언급되었습니다.
omikron

7
@omikron 그러나 파이썬의 스레드는이 기능을 가능하게하는 서브 클래스를 사용하지 않으면 응답을 반환하지 않습니다. 가능한 서브 클래스 중에서 ThreadPools는 훌륭한 선택입니다 (스레드 수 선택, 맵 / 적용 w / sync / async 사용). 에서 가져 왔음에도 불구하고 multiprocess프로세스와 관련이 없습니다.
Jake Biesinger

4
@JakeBiesinger 오, 나는 장님입니다. 불필요한 의견에 대해 죄송합니다. 네 말이 맞아 방금 다중 처리 = 프로세스라고 가정했습니다.
omikron

12
processes=1더 많은 스레드가있는 경우 둘 이상 으로 설정 하는 것을 잊지 마십시오 !
iman

4
멀티 프로세싱 및 스레드 풀의 문제점은 기본 스레딩 라이브러리에 비해 스레드를 설정하고 시작하는 것이 훨씬 느리다는 것입니다. 오래 실행되는 스레드를 시작하는 데는 좋지만 짧은 실행 스레드를 많이 시작해야 할 때는 목적을 무시하십시오. 다른 답변에 문서화 된 "스레딩"과 "대기열"을 사용하는 솔루션은 후자의 유스 케이스에 대한 더 나은 대안이라고 생각합니다.
Yves Dorfsman

242

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

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

10
큰 기차 농담.
meawoppl

7
python3에서는을 반환합니다 TypeError: __init__() takes from 1 to 6 positional arguments but 7 were given. 그것을 고칠 방법이 있습니까?
GuySoft

2
이 중 두 번째 ( _Thread__target일)를 하고 싶은 사람에게 경고하십시오 . 코드를 파이썬 3으로 포팅하려는 모든 사람은 당신이 한 일을 해결할 때까지 (2와 3 사이에서 변경된 문서화되지 않은 기능을 사용하기 때문에) 당신을 미워하게 할 것입니다. 코드를 잘 문서화하십시오.
벤 테일러

84

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 오브젝트를 리턴합니다.

최신 정보

이 답변을 게시한지 꽤 오래되었지만 여전히 뷰를 얻으므로 최신 버전의 Python 에서이 작업을 수행하는 방식을 반영하여 업데이트 할 것이라고 생각했습니다.

concurrent.futures병렬 작업을위한 고급 인터페이스를 제공하는 Python 3.2가 모듈에 추가되었습니다 . 그것은 제공 ThreadPoolExecutorProcessPoolExecutor같은 API를 사용하여 스레드 또는 프로세스 풀을 사용할 수 있습니다.

이 API의 장점 중 하나 인에 작업을 제출하는 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 객체)을 사용하면 정말 멋진 기능 중 하나는 반환 된 미래를 감싸서 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

1
코드는 명시 적으로 잠금을 사용하지 않으므로 코드의 다른 곳에 문제가있을 수 있습니다. 당신은 그것에 관한 새로운 SO 질문을 게시하고 싶을 수도 있습니다
bj0

result_queue가 인스턴스 속성 인 이유는 무엇입니까? @threaded를 사용할 때 명시적이고 모호하지 않은 @threaded를 사용할 때 사용자가 result_queue를 호출 할 필요가 없도록 클래스 속성이라면 더 좋을까요?
nonbot

@ t88, 무슨 뜻인지 확실하지 않으면 결과에 액세스하는 방법이 필요하므로 전화해야 할 것을 알아야합니다. 다른 것을 원한다면 Thread를 서브 클래스 화하고 원하는 것을 할 수 있습니다 (간단한 해결책이었습니다). 대기열을 스레드에 연결해야하는 이유는 여러 호출 / 함수가 자체 대기열을
갖기 때문입니다.

1
훌륭합니다! 대단히 감사합니다.
Ganesh Kathiresan

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

6
당신의 고향에 당신의 동상이 있어야합니다, 감사합니다!
Onilol

3
@Onilol-대단히 감사합니다. 귀하의 의견은 정확히 내가 이것을하는 이유입니다 :)
Arik

4
Python3의 경우로 변경해야 from queue import Queue합니다.
지노 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

1, 참고 Thread클래스는 파이썬 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 ()에 대한 이해력이 부족하다는이 질문에 대한 많은 답변이 있습니다. 일부는 timeout인수 를 완전히 처리하지 못합니다 . 그러나 (1) 반환 할 수있는 대상 함수가 None있고 (2) timeoutarg를 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))

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

문제는 giveMe ()가 None을 리턴 할 것으로 예상하지만 (TEST 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 년

당신이 저장할 경우, 스레드 클래스의 private 변수에 접근 피할 수 target, argskwargs인수를 초기화하기 클래스에 멤버 변수로.
Tolli

@GreySage 내 답변을 참조하십시오. 이 블록을 아래의 python3으로 포팅했습니다
GuySoft

@GreySage 답변은 이제 py2 및 py3를 지원합니다
user2426679

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
이 솔루션은 정말 짧고 달콤합니다. 함수가 입력 대기열을 읽고 추가 하면 Queue.Empty 예외 out_queue1를 반복 out_queue1.get()하여 잡아야합니다 ret = [] ; try: ; while True; ret.append(out_queue1.get(block=False)) ; except Queue.Empty: ; pass. 줄 바꿈을 시뮬레이션하는 세미콜론
sastorsl

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이라고 가정 해 봅시다. 모든 스레드가 실패하거나 하나만 실패합니까?
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에서 복사되었습니다. 예를 들어 여전히 쓸모없는 1st (!) 생성자 매개 변수에서 볼 수 group있습니다.


첫 번째 생성자는 파이썬 병렬 프로그래밍 요리 책에서 .. 미래 구현을 위해 거기 예약, 쓸모없는
비제이 단도

1

당신의 목표를 정의
인수 소요) 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수정하지 않고 사용할 수 있지만이 기본 체계를 따르십시오.


그것은해야한다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

일반적인 해결책 중 하나는 다음 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


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

Kindall의 대답 Python3에서

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 

필요한 조치를 취하기 위해 스레드 중 하나가 잘못된 상태를 리턴했는지 확인하려는 경우 더 유용합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.