Làm thế nào để có được giá trị trả về từ một luồng trong python?


342

Hàm foodưới đây trả về một chuỗi 'foo'. Làm thế nào tôi có thể nhận được giá trị 'foo'được trả về từ mục tiêu của luồng?

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

"Một cách rõ ràng để làm điều đó", được hiển thị ở trên, không hoạt động: thread.join()trả lại None.

Câu trả lời:


37

Trong Python 3.2+, concurrent.futuresmô-đun stdlib cung cấp API cấp cao hơn threading, bao gồm chuyển các giá trị trả về hoặc ngoại lệ từ luồng công nhân trở lại luồng chính:

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)

Đối với những người tự hỏi điều này có thể được thực hiện với một danh sách các chủ đề. futures = [executor.submit(foo, param) for param in param_list]Thứ tự sẽ được duy trì và thoát khỏi withsẽ cho phép thu thập kết quả. [f.result() for f in futures]
jayreed1

273

FWIW, multiprocessingmô-đun có một giao diện đẹp cho việc này bằng cách sử dụng Poollớp. Và nếu bạn muốn gắn bó với các luồng hơn là các tiến trình, bạn chỉ có thể sử dụng multiprocessing.pool.ThreadPoollớp này như một sự thay thế thả vào.

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 Quan điểm của tôi là, tôi đang tìm kiếm câu trả lời, làm thế nào để nhận được phản hồi từ Thread, đến đây và câu trả lời được chấp nhận không trả lời câu hỏi đã nêu. Tôi phân biệt chủ đề và quy trình. Tôi biết về Khóa phiên dịch toàn cầu tuy nhiên tôi đang làm việc với vấn đề ràng buộc I / O nên Chủ đề vẫn ổn, tôi không cần các quy trình. Câu trả lời khác ở đây câu trả lời tốt hơn nêu.
omikron

7
@omikron Nhưng các luồng trong python không trả về phản hồi trừ khi bạn sử dụng lớp con cho phép chức năng này. Trong số các lớp con có thể, ThreadPool là một lựa chọn tuyệt vời (chọn # chủ đề, sử dụng bản đồ / áp dụng w / sync / async). Mặc dù được nhập từ multiprocess, chúng không liên quan gì đến Processes.
Jake Biesinger

4
@JakeBiesinger ơi, tôi bị mù rồi. Xin lỗi vì những bình luận không cần thiết của tôi. Bạn đúng rồi. Tôi chỉ giả định rằng đa xử lý = quy trình.
omikron

12
Đừng quên đặt processes=1thành nhiều hơn một nếu bạn có nhiều chủ đề!
iman

4
Vấn đề với đa xử lý và nhóm luồng là việc thiết lập và bắt đầu các luồng chậm hơn nhiều so với thư viện luồng cơ bản. Thật tuyệt khi bắt đầu các chuỗi chạy dài nhưng đánh bại mục đích khi cần bắt đầu nhiều luồng chạy ngắn. Giải pháp sử dụng "luồng" và "Hàng đợi" được ghi lại trong các câu trả lời khác ở đây là một giải pháp thay thế tốt hơn cho trường hợp sử dụng sau này theo ý kiến ​​của tôi.
Yves Dorfsman

242

Một cách tôi đã thấy là chuyển một đối tượng có thể thay đổi, chẳng hạn như danh sách hoặc từ điển, đến hàm tạo của luồng, cùng với một chỉ mục hoặc mã định danh khác thuộc loại nào đó. Sau đó, luồng có thể lưu trữ kết quả của nó trong khe chuyên dụng của nó trong đối tượng đó. Ví dụ:

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?

Nếu bạn thực sự muốn join()trả về giá trị trả về của hàm được gọi, bạn có thể thực hiện việc này với một Threadlớp con như sau:

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

Điều đó có một chút lông vì một số tên xáo trộn, và nó truy cập các cấu trúc dữ liệu "riêng tư" dành riêng cho Threadviệc thực hiện ... nhưng nó hoạt động.

Cho trăn3

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
tuyệt, cảm ơn cho ví dụ! Tôi tự hỏi tại sao Thread không được thực hiện với việc xử lý giá trị trả về ở vị trí đầu tiên, nó có vẻ như là một điều đủ rõ ràng để hỗ trợ.
wim

16
Tôi nghĩ rằng đây phải là câu trả lời được chấp nhận - OP yêu cầu threading, không phải là một thư viện khác để thử, cộng với giới hạn kích thước nhóm giới thiệu một vấn đề tiềm năng bổ sung, xảy ra trong trường hợp của tôi.
domoarigato

10
Trò đùa tàu tuyệt vời.
meawoppl

7
Trên python3 này trả về TypeError: __init__() takes from 1 to 6 positional arguments but 7 were given. Có cách nào khắc phục điều đó không?
GuySoft

2
Cảnh báo cho bất cứ ai muốn làm thứ hai trong số này ( _Thread__targetđiều). Bạn sẽ khiến bất cứ ai cố gắng chuyển mã của bạn sang python 3 ghét bạn cho đến khi họ tìm ra những gì bạn đã làm (vì sử dụng các tính năng không có giấy tờ đã thay đổi giữa 2 và 3). Tài liệu mã của bạn tốt.
Ben Taylor

84

Câu trả lời của Jake là tốt, nhưng nếu bạn không muốn sử dụng một luồng (bạn không biết bạn cần bao nhiêu luồng, nhưng tạo chúng khi cần) thì một cách tốt để truyền thông tin giữa các luồng là tích hợp Lớp Queue.Queue , vì nó cung cấp sự an toàn của luồng.

Tôi đã tạo ra trình trang trí sau đây để làm cho nó hoạt động theo kiểu tương tự như luồng:

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

Sau đó, bạn chỉ cần sử dụng nó như:

@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

Hàm được trang trí tạo ra một luồng mới mỗi lần nó được gọi và trả về một đối tượng Thread chứa hàng đợi sẽ nhận kết quả.

CẬP NHẬT

Đã khá lâu kể từ khi tôi đăng câu trả lời này, nhưng nó vẫn nhận được lượt xem vì vậy tôi nghĩ rằng tôi sẽ cập nhật nó để phản ánh cách tôi làm điều này trong các phiên bản Python mới hơn:

Python 3.2 được thêm vào trong concurrent.futuresmô-đun cung cấp giao diện cấp cao cho các tác vụ song song. Nó cung cấp ThreadPoolExecutorProcessPoolExecutor, vì vậy bạn có thể sử dụng một luồng hoặc xử lý nhóm với cùng một api.

Một lợi ích của api này là việc gửi một tác vụ tới Executormột Futuređối tượng trả về , nó sẽ hoàn thành với giá trị trả về của hàm có thể gọi được mà bạn gửi.

Điều này làm cho việc gắn một queueđối tượng không cần thiết, giúp đơn giản hóa trang trí khá nhiều:

_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

Điều này sẽ sử dụng một trình thực thi luồng mô-đun mặc định nếu một không được truyền vào.

Cách sử dụng rất giống với trước đây:

@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

Nếu bạn đang sử dụng Python 3.4 trở lên, một trong những tính năng thật sự tốt đẹp của việc sử dụng phương pháp này (và các đối tượng trong tương lai nói chung) là tương lai trở lại có thể được bao bọc để biến nó thành một asyncio.Futurevới asyncio.wrap_future. Điều này làm cho nó hoạt động dễ dàng với coroutines:

result = await asyncio.wrap_future(long_task(10))

Nếu bạn không cần quyền truy cập vào concurrent.Futuređối tượng cơ bản , bạn có thể bao gồm phần bọc trong trang trí:

_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

Sau đó, bất cứ khi nào bạn cần đẩy cpu chuyên sâu hoặc chặn mã khỏi chuỗi vòng lặp sự kiện, bạn có thể đặt nó vào một chức năng được trang trí:

@threadpool
def some_long_calculation():
    ...

# this will suspend while the function is executed on a threadpool
result = await some_long_calculation()

Tôi dường như không thể làm cho điều này để làm việc; Tôi nhận được một lỗi cho biết AttributeError: 'module' object has no attribute 'Lock'điều này dường như được phát ra từ dòng y = long_task(10)... suy nghĩ?
sadmic [

1
Mã không sử dụng rõ ràng Khóa, vì vậy vấn đề có thể ở một nơi khác trong mã của bạn. Bạn có thể muốn đăng một câu hỏi SO mới về nó
bj0

Tại sao result_queue là một thuộc tính thể hiện? Sẽ tốt hơn nếu đó là một thuộc tính lớp để người dùng sẽ không phải gọi result_queue khi sử dụng @threaded không rõ ràng và mơ hồ?
nonbot

@ t88, không chắc ý của bạn là gì, bạn cần một số cách truy cập kết quả, có nghĩa là bạn cần biết phải gọi gì. Nếu bạn muốn nó là một cái gì đó khác, bạn có thể phân lớp Thread và làm những gì bạn muốn (đây là một giải pháp đơn giản). Lý do hàng đợi cần được gắn vào luồng là để nhiều cuộc gọi / hàm có hàng đợi riêng
bj0

1
Thật là tuyệt vời! Cảm ơn rât nhiều.
Ganesh Kathiresan

53

Một giải pháp khác không yêu cầu thay đổi mã hiện tại của bạn:

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

Nó cũng có thể dễ dàng điều chỉnh theo môi trường đa luồng:

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, '! thế giới')) whats q.put làm gì ở đây, những gì hiện Queue.Queue () thực hiện
vijay shanker

6
Nên có một bức tượng của bạn ở quê hương của bạn, cảm ơn bạn!
Onilol

3
@Onilol - Cảm ơn bạn rất nhiều. Nhận xét của bạn chính xác là lý do tôi làm điều này :)
Arik

4
Đối với Python3, cần thay đổi thành from queue import Queue.
Gino Mempin

1
Đây dường như là phương pháp ít gây gián đoạn nhất (không cần cơ cấu lại đáng kể cơ sở mã gốc) để cho phép giá trị trả về quay lại luồng chính.
Fanchen Bao

24

Câu trả lời join / câu trả lời của Parris / kindall returnđược chuyển sang Python 3:

from threading import Thread

def foo(bar):
    print('hello {0}'.format(bar))
    return "foo"

class ThreadWithReturnValue(Thread):
    def __init__(self, group=None, target=None, name=None, args=(), kwargs=None, *, daemon=None):
        Thread.__init__(self, group, target, name, args, kwargs, daemon=daemon)

        self._return = None

    def run(self):
        if self._target is not None:
            self._return = self._target(*self._args, **self._kwargs)

    def join(self):
        Thread.join(self)
        return self._return


twrv = ThreadWithReturnValue(target=foo, args=('world!',))

twrv.start()
print(twrv.join())   # prints foo

Lưu ý, Threadlớp được triển khai khác nhau trong Python 3.


1
tham gia mất một tham số thời gian chờ nên được chuyển qua
cz

22

Tôi đã đánh cắp câu trả lời của kindall và làm sạch nó chỉ một chút.

Phần quan trọng là thêm * args và ** kwargs để tham gia () để xử lý thời gian chờ

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

CẬP NHẬT TRẢ LỜI DƯỚI ĐÂY

Đây là câu trả lời phổ biến nhất của tôi, vì vậy tôi quyết định cập nhật với mã sẽ chạy trên cả py2 và py3.

Ngoài ra, tôi thấy nhiều câu trả lời cho câu hỏi này cho thấy sự thiếu hiểu biết về Thread.join (). Một số hoàn toàn không xử lý các timeoutarg. Nhưng cũng có một trường hợp góc mà bạn cần lưu ý về các trường hợp khi bạn có (1) một hàm mục tiêu có thể trả về Nonevà (2) bạn cũng chuyển đối số timeoutđể tham gia (). Vui lòng xem "KIỂM TRA 4" để hiểu trường hợp góc này.

Lớp ThreadWithReturn hoạt động với py2 và py3:

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

Một số thử nghiệm mẫu được hiển thị dưới đây:

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

Bạn có thể xác định trường hợp góc mà chúng ta có thể gặp phải với TEST 4 không?

Vấn đề là chúng tôi hy vọng giveMe () sẽ trả về Không (xem TEST 2), nhưng chúng tôi cũng mong tham gia () sẽ trả về Không nếu hết thời gian.

returned is None có nghĩa là:

(1) đó là những gì giveMe () đã trả lại hoặc

(2) tham gia () đã hết thời gian

Ví dụ này là tầm thường vì chúng ta biết rằng giveMe () sẽ luôn trả về Không. Nhưng trong trường hợp thực tế (nơi mục tiêu có thể trả lại hợp pháp Không có gì hoặc điều gì khác), chúng tôi muốn kiểm tra rõ ràng những gì đã xảy ra.

Dưới đây là cách giải quyết trường hợp góc này:

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

Bạn có biết _Thread_target tương đương với Python3 không? Thuộc tính đó không tồn tại trong Python3.
GreySage

Tôi đã xem trong tập tin threading.py, hóa ra đó là _target (các thuộc tính khác được đặt tên tương tự).
GreySage

Bạn có thể tránh truy cập vào các biến private của lớp chủ đề, nếu bạn lưu target, argskwargslập luận để init như biến thành viên trong lớp học của bạn.
Tolli

@GreySage Xem câu trả lời của tôi, tôi đã chuyển khối này sang python3 bên dưới
GuySoft

Câu trả lời @GreySage hiện hỗ trợ py2 và py3
user2426679

15

Sử dụng hàng đợi:

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
Thực sự thích sự châm biếm này, ngắn và ngọt ngào. Nếu chức năng của bạn đọc một hàng đợi đầu vào và bạn thêm vào đó, out_queue1bạn sẽ cần phải lặp lại out_queue1.get()và bắt ngoại lệ Queue.Empty : ret = [] ; try: ; while True; ret.append(out_queue1.get(block=False)) ; except Queue.Empty: ; pass. Semi-colons để mô phỏng ngắt dòng.
sastorsl

6

Giải pháp của tôi cho vấn đề này là bọc hàm và luồng trong một lớp. Không yêu cầu sử dụng nhóm, hàng đợi hoặc biến loại c đi qua. Nó cũng không chặn. Bạn kiểm tra trạng thái thay thế. Xem ví dụ về cách sử dụng nó ở cuối mã.

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

Làm thế nào bạn sẽ xử lý một ngoại lệ? giả sử hàm add được đưa ra và int và str. tất cả các chủ đề sẽ thất bại hoặc chỉ có một thất bại?
dùng1745613

4

joinluôn luôn trả về None, tôi nghĩ bạn nên phân lớp Threadđể xử lý mã trả về và vì vậy.


4

Cân nhắc @iman bình luận về câu trả lời @JakeBiesinger Tôi đã viết lại nó để có số lượng chủ đề khác nhau:

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

Chúc mừng

Chàng.


2

Bạn có thể định nghĩa một biến đổi trên phạm vi của hàm luồng và thêm kết quả vào đó. (Tôi cũng đã sửa đổi mã để tương thích với 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)

Điều này trở lại {'world!': 'foo'}

Nếu bạn sử dụng đầu vào chức năng làm chìa khóa cho kết quả của mình, mọi đầu vào duy nhất được đảm bảo để đưa ra một mục trong kết quả


2

Tôi đang sử dụng trình bao bọc này, giúp thoải mái chuyển bất kỳ chức năng nào để chạy trong Thread- chăm sóc giá trị trả về hoặc ngoại lệ của nó. Nó không thêm Queuechi phí.

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

Ví dụ sử dụng

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

Ghi chú về threadingmô-đun

Giá trị trả về thoải mái và xử lý ngoại lệ của hàm luồng là nhu cầu "Pythonic" thường xuyên và thực sự đã được cung cấp bởi threadingmô-đun - có thể trực tiếp trong Threadlớp tiêu chuẩn . ThreadPoolcó quá nhiều chi phí cho các nhiệm vụ đơn giản - 3 luồng quản lý, rất nhiều quan liêu. Thật không may Thread, bố cục của nó đã được sao chép từ Java ban đầu - mà bạn thấy, ví dụ như từ tham số hàm tạo 1st (!) Vẫn vô dụng group.


hàm tạo đầu tiên không phải là vô dụng, nó được dành riêng để thực hiện trong tương lai .. từ sách dạy nấu ăn lập trình song song python
vijay shanker

1

Xác định mục tiêu của bạn thành
1) lấy một đối số q
2) thay thế bất kỳ câu lệnh nào return foobằngq.put(foo); return

một chức năng

def func(a):
    ans = a * a
    return ans

sẽ trở thành

def func(a, q):
    ans = a * a
    q.put(ans)
    return

và sau đó bạn sẽ tiến hành như vậy

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

Và bạn có thể sử dụng các hàm trang trí / hàm bao để làm cho nó để bạn có thể sử dụng các hàm hiện có của mình targetmà không cần sửa đổi chúng, nhưng hãy làm theo sơ đồ cơ bản này.


Nó nên làresults = [ans_q.get() for _ in xrange(len(threads))]
Hemant H Kumar

1

Như đã đề cập nhóm đa xử lý chậm hơn nhiều so với phân luồng cơ bản. Sử dụng hàng đợi như đề xuất trong một số câu trả lời ở đây là một cách thay thế rất hiệu quả. Tôi đã sử dụng nó với từ điển để có thể chạy rất nhiều chủ đề nhỏ và thu hồi nhiều câu trả lời bằng cách kết hợp chúng với từ điển:

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

Ý tưởng của GuySoft là tuyệt vời, nhưng tôi nghĩ rằng đối tượng không nhất thiết phải kế thừa từ Thread và start () có thể bị xóa khỏi giao diện:

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

Một giải pháp thông thường là bọc chức năng của bạn foovới một trình trang trí như

result = queue.Queue()

def task_wrapper(*args):
    result.put(target(*args))

Sau đó, toàn bộ mã có thể trông như thế

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

Ghi chú

Một vấn đề quan trọng là các giá trị trả về có thể không được điều chỉnh . (Trong thực tế, return valuekhông nhất thiết phải được lưu vào queue, vì bạn có thể chọn cấu trúc dữ liệu an toàn luồng tùy ý )


0

Tại sao không sử dụng biến toàn cầu?

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

Câu trả lời của Kindall trong 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

Nếu chỉ có Đúng hoặc Sai được xác thực từ lệnh gọi của hàm, một giải pháp đơn giản hơn mà tôi tìm thấy là cập nhật danh sách toàn cầu.

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 

Điều này hữu ích hơn khi bạn muốn tìm nếu bất kỳ một trong các luồng đã trả về trạng thái sai để thực hiện hành động cần thiết.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.