अजगर में एक धागे से वापसी मूल्य कैसे प्राप्त करें?


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

पायथन 3.2+ में, stdlib concurrent.futuresमॉड्यूल एक उच्च स्तर की API प्रदान करता है threading, जिसमें रिटर्न मान या कार्यकर्ता थ्रेड से अपवाद को मुख्य थ्रेड में शामिल करना शामिल है:

import concurrent.futures

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

with concurrent.futures.ThreadPoolExecutor() as executor:
    future = executor.submit(foo, 'world!')
    return_value = future.result()
    print(return_value)

सोच रहे लोगों के लिए यह धागे की एक सूची के साथ किया जा सकता है। futures = [executor.submit(foo, param) for param in param_list]आदेश बनाए रखा जाएगा, और withवसीयत से बाहर निकलने के परिणाम संग्रह की अनुमति देगा। [f.result() for f in futures]
jayreed1

273

FWIW, multiprocessingमॉड्यूल में Poolकक्षा का उपयोग करने के लिए एक अच्छा इंटरफ़ेस है । और अगर आप प्रक्रियाओं के बजाय थ्रेड्स के साथ रहना चाहते हैं, तो आप multiprocessing.pool.ThreadPoolक्लास को ड्रॉप-इन प्रतिस्थापन के रूप में उपयोग कर सकते हैं ।

def foo(bar, baz):
  print 'hello {0}'.format(bar)
  return 'foo' + baz

from multiprocessing.pool import ThreadPool
pool = ThreadPool(processes=1)

async_result = pool.apply_async(foo, ('world', 'foo')) # tuple of args for foo

# do some other stuff in the main process

return_val = async_result.get()  # get the return value from your function.

50
@JakeBiesinger मेरी बात यह है, कि मैं उत्तर की तलाश में था, थ्रेड से प्रतिक्रिया कैसे प्राप्त करें, यहाँ आया था, और स्वीकृत उत्तर प्रश्न का उत्तर नहीं दिया। मैं धागे और प्रक्रियाओं को अलग करता हूं। मैं ग्लोबल इंटरप्रेटर लॉक के बारे में जानता हूं, हालांकि मैं I / O बाध्य समस्या पर काम कर रहा हूं, इसलिए थ्रेड्स ठीक हैं, मुझे प्रक्रियाओं की आवश्यकता नहीं है। अन्य उत्तर यहां बेहतर उत्तर प्रश्न कहा गया है।
ओमिक्रॉन

7
@omikron लेकिन अजगर में धागे एक प्रतिक्रिया नहीं देते हैं जब तक कि आप एक उपवर्ग का उपयोग नहीं करते हैं जो इस कार्यक्षमता को सक्षम करता है। संभावित उपवर्गों में, थ्रेडपूल एक बढ़िया विकल्प है (# थ्रेड का चयन करें, मैप का उपयोग करें / w / सिंक / async का उपयोग करें)। से आयात किए जाने के बावजूद multiprocess, उन्हें प्रक्रियाओं से कोई लेना-देना नहीं है।
जेक बिसिंगर

4
@JakeBiesinger ओह, मैं अंधा हूं। मेरी अनावश्यक टिप्पणियों के लिए क्षमा करें। तुम सही हो। मैंने सिर्फ यह माना कि बहुक्रिया = प्रक्रियाएँ।
ओमिक्रॉन

12
processes=1यदि आपके पास अधिक धागे हैं, तो एक से अधिक सेट करने के लिए मत भूलना !
इमान

4
मल्टीप्रोसेसिंग और थ्रेड पूल के साथ समस्या यह है कि यह मूल थ्रेडिंग लाइब्रेरी की तुलना में थ्रेड को सेटअप करने और शुरू करने के लिए बहुत धीमा है। यह लंबे समय तक चलने वाले धागे को शुरू करने के लिए बहुत अच्छा है, लेकिन बहुत कम चलने वाले धागे को शुरू करने की आवश्यकता होने पर उद्देश्य को हरा दें। अन्य उत्तरों में प्रलेखित "थ्रेडिंग" और "क्यू" का उपयोग करने का समाधान मेरी राय में उस बाद के उपयोग के मामले के लिए एक बेहतर विकल्प है।
यवेस डोरफ्समैन

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कार्यान्वयन के लिए विशिष्ट हैं ... लेकिन यह काम करता है।

पायथन 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):
        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
शांत, उदाहरण के लिए धन्यवाद! मुझे आश्चर्य है कि थ्रेड को पहली जगह में वापसी मूल्य को संभालने के साथ क्यों नहीं लागू किया गया था, ऐसा लगता है कि समर्थन करने के लिए एक स्पष्ट पर्याप्त चीज है।
wim

16
मुझे लगता है कि यह स्वीकृत उत्तर होना चाहिए - ओपी ने कहा threading, कोशिश करने के लिए एक अलग पुस्तकालय नहीं है, साथ ही पूल आकार सीमा एक अतिरिक्त संभावित समस्या का परिचय देता है, जो मेरे मामले में हुआ था।
डोमोइरैगेटो

10
महान ट्रेन मजाक।
मेवोप्लप

7
Python3 पर यह रिटर्न देता है TypeError: __init__() takes from 1 to 6 positional arguments but 7 were given। इसे ठीक करने का कोई तरीका?
गाईसॉफ्ट

2
किसी के लिए चेतावनी इन ( _Thread__targetबात) का दूसरा करने के लिए लुभाती है । आप किसी को भी अपने कोड को python 3 में पोर्ट करने की कोशिश करेंगे, जब तक कि वे आपके द्वारा किए गए काम से नफरत न करें (क्योंकि 2 और 3 के बीच बदल गई अनिर्दिष्ट सुविधाओं का उपयोग करने के कारण)। अपने कोड को अच्छी तरह से प्रलेखित करें।
बेन टेलर

84

जेक का उत्तर अच्छा है, लेकिन यदि आप थ्रेडपूल का उपयोग नहीं करना चाहते हैं (आप नहीं जानते कि आपको कितने थ्रेड्स की आवश्यकता होगी, लेकिन उन्हें आवश्यकतानुसार बनाएं) तो थ्रेड्स के बीच जानकारी संचारित करने का एक अच्छा तरीका अंतर्निहित है क्यू क्यू। क्यू वर्ग, क्योंकि यह थ्रेड सुरक्षा प्रदान करता है।

मैंने थ्रेडपोल के समान शैली में अभिनय करने के लिए निम्नलिखित डेकोरेटर बनाया:

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

सजाए गए फ़ंक्शन को हर बार एक नया थ्रेड बनाया जाता है जिसे यह कहा जाता है और एक थ्रेड ऑब्जेक्ट देता है जिसमें कतार होती है जो परिणाम प्राप्त करेगी।

अपडेट करें

जब से मैंने इस उत्तर को पोस्ट किया है, तब तक यह काफी समय हो चुका है, लेकिन यह अभी भी विचार प्राप्त करता है इसलिए मैंने सोचा कि मैं इसे अद्यतन करूंगा कि मैं इसे पायथन के नए संस्करणों में कैसे प्रदर्शित करूं:

अजगर 3.2 concurrent.futuresमॉड्यूल में जोड़ा गया है जो समानांतर कार्यों के लिए एक उच्च-स्तरीय इंटरफ़ेस प्रदान करता है। यह प्रदान करता है ThreadPoolExecutorऔर ProcessPoolExecutor, तो आप एक ही एपीआई के साथ एक धागा या प्रक्रिया पूल का उपयोग कर सकते हैं।

इस एप का एक लाभ यह है कि किसी कार्य को किसी वस्तु को 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

यदि आप पायथॉन 3.4+ का उपयोग कर रहे हैं, तो इस पद्धति (और सामान्य रूप से भविष्य की वस्तुओं) का उपयोग करने की एक बहुत अच्छी विशेषता यह है कि लौटे भविष्य को इसे एक asyncio.Futureसाथ चालू करने के लिए लपेटा जा सकता है 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

फिर, जब भी आपको ईवेंट लूप थ्रेड से सीपीयू गहन या अवरुद्ध कोड को पुश करने की आवश्यकता होती है, तो आप इसे एक सजाए गए फ़ंक्शन में रख सकते हैं:

@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)... विचारों से निकलती प्रतीत होती है ?
सैडमिक माइक्रोवेव 13'13

1
कोड स्पष्ट रूप से लॉक का उपयोग नहीं करता है, इसलिए समस्या आपके कोड में कहीं और हो सकती है। आप इसके बारे में एक नया SO प्रश्न पोस्ट करना चाहते हैं
bj0

Result_queue एक उदाहरण विशेषता क्यों है? क्या यह बेहतर होगा अगर यह एक वर्ग विशेषता थी ताकि उपयोगकर्ताओं को @threaded का उपयोग करते समय result_queue को कॉल करने के लिए जानना न पड़े जो कि स्पष्ट और अस्पष्ट नहीं है?
नॉनबॉट

@ t88, यह निश्चित नहीं है कि आपका क्या मतलब है, आपको परिणाम तक पहुंचने के कुछ तरीके की आवश्यकता है, जिसका अर्थ है कि आपको यह जानना होगा कि क्या कॉल करना है। यदि आप चाहते हैं कि यह कुछ और हो तो आप थ्रेड को उपवर्गित कर सकते हैं और वही कर सकते हैं जो आप चाहते हैं (यह एक सरल उपाय था)। कतार को धागे से
जोड़ने की

1
ये जबरदस्त है! आपका बहुत बहुत धन्यवाद।
गणेश कथायर्सन

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 = थ्रेड (लक्ष्य = लैम्ब्डा q, arg1: q.put (foo (arg1)), args = (क्यू, 'वर्ल्ड!')) whats q.put यहाँ क्या कर रहा है, क्या Queue.Queue () करता है
vijay! शंकर

6
आपके गृहनगर में आपकी एक प्रतिमा होनी चाहिए, धन्यवाद!
ओनीलोल

3
@ ओनीलोल - बहुत बहुत धन्यवाद। आपकी टिप्पणी ठीक यही कारण है कि मैं ऐसा कर रहा हूं :)
अरीक

4
Python3 के लिए, को बदलने की आवश्यकता है from queue import Queue
गीनो मेम्पिन

1
यह कम से कम विघटनकारी विधि (मूल कोड आधार को नाटकीय रूप से पुनर्गठन करने की आवश्यकता नहीं) लगता है ताकि मुख्य थ्रेड पर वापस आने वाले मूल्य को वापस लाया जा सके।
23

24

पैरिस / किंडल के उत्तर join / returnउत्तर को पायथन 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कक्षा को पायथन 3 में अलग तरह से लागू किया गया है।


1
ज्वाइन टाइमटाइम पैरामीटर लेता है जिसे साथ में पारित किया जाना चाहिए
cz

22

मैंने किंडल के जवाब को चुरा लिया और इसे थोड़ा साफ कर दिया।

टाइमआउट को संभालने के लिए इसमें () और शामिल होने के लिए (*) को जोड़ने के लिए मुख्य भाग * आर्ग और ** क्वार्ग्स जोड़ रहा है

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आर्गन को संभालने में विफल रहते हैं । लेकिन एक कोने-मामला यह भी है कि आपको ऐसे उदाहरणों के बारे में पता होना चाहिए जब आपके पास (1) एक लक्ष्य फ़ंक्शन होता है जो वापस आ सकता है Noneऔर (2) आप भी timeoutशामिल होने के लिए आर्ग पास करते हैं ()। कृपया इस कोने के मामले को समझने के लिए "TEST 4" देखें।

थ्रेडविथर्न क्लास जो py2 और 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

कुछ नमूने परीक्षण नीचे दिखाए गए हैं:

import time, random

# TEST TARGET FUNCTION
def giveMe(arg, seconds=None):
    if not seconds is None:
        time.sleep(seconds)
    return arg

# TEST 1
my_thread = ThreadWithReturn(target=giveMe, args=('stringy',))
my_thread.start()
returned = my_thread.join()
# (returned == 'stringy')

# TEST 2
my_thread = ThreadWithReturn(target=giveMe, args=(None,))
my_thread.start()
returned = my_thread.join()
# (returned is None)

# TEST 3
my_thread = ThreadWithReturn(target=giveMe, args=('stringy',), kwargs={'seconds': 5})
my_thread.start()
returned = my_thread.join(timeout=2)
# (returned is None) # because join() timed out before giveMe() finished

# TEST 4
my_thread = ThreadWithReturn(target=giveMe, args=(None,), kwargs={'seconds': 5})
my_thread.start()
returned = my_thread.join(timeout=random.randint(1, 10))

क्या आप उस कोने-मामले की पहचान कर सकते हैं जिसका सामना संभवतः TEST 4 से हो सकता है?

समस्या यह है कि हम उम्मीद करते हैं कि कोई भी रिटर्न (TEST 2 देखें) नहीं दे सकता, लेकिन हम यह भी उम्मीद करते हैं कि अगर इसमें से कोई भी वापस लौटता है तो वह इसमें शामिल हो जाए।

returned is None या तो इसका मतलब है:

(1) यह है कि क्या दे () लौटे, या

(२) जुड़ना () समय समाप्त होना

यह उदाहरण तुच्छ है क्योंकि हम जानते हैं कि GiveMe () हमेशा कोई नहीं लौटेगा। लेकिन वास्तविक दुनिया में उदाहरण (जहां लक्ष्य वैध रूप से कोई भी या कुछ और नहीं लौटा सकता है) हम स्पष्ट रूप से जांचना चाहते हैं कि क्या हुआ।

नीचे इस कोने-मामले को संबोधित करने का तरीका बताया गया है:

# 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 बराबर है? पायथन 3 में वह विशेषता मौजूद नहीं है।
ग्रेनेज

मैंने थ्रेडिंगहोम फ़ाइल में देखा, यह पता चलता है कि यह _target है (अन्य विशेषताओं को समान रूप से नाम दिया गया है)।
ग्रेसेज

यदि आप अपनी कक्षा में सदस्य चर के रूप में init को सहेजते हैं , और बहस करते हैं target, तो आप थ्रेड क्लास के निजी चर तक पहुँचने से बच सकते हैं । argskwargs
तोली

@GreySage मेरा उत्तर देखें, मैंने इस ब्लॉक को नीचे
python3 में चित्रित किया

@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
वास्तव में इस तरह की नजाकत, छोटी और प्यारी। यदि आपका फ़ंक्शन एक इनपुट कतार पढ़ता है, और आप जोड़ते हैं तो out_queue1आपको लूप पर out_queue1.get()और क्यू को पकड़ने की आवश्यकता होगी ret = [] ; try: ; while True; ret.append(out_queue1.get(block=False)) ; except Queue.Empty: ; pass। अतिरिक्त अपवाद :। सेमी-कॉलोन्स को लाइन ब्रेक का अनुकरण करने के लिए।
sastorsl

6

समस्या का मेरा समाधान एक कक्षा में फ़ंक्शन और थ्रेड को लपेटना है। पूल, कतारों या सी प्रकार के चर का उपयोग करने की आवश्यकता नहीं है। यह भी न के बराबर है। आप इसके बजाय स्थिति की जाँच करें। कोड के अंत में इसका उपयोग कैसे करें, इसका उदाहरण देखें।

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

आप एक अपवाद को कैसे संभालेंगे? मान लीजिए कि ऐड फंक्शन दिया गया था और int और a str। क्या सभी धागे विफल होंगे या केवल एक ही असफल होगा?
user1745713

4

joinहमेशा वापस लौटें 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मॉड्यूल पर नोट्स

थ्रेडेड फ़ंक्शन के आरामदायक रिटर्न वैल्यू और अपवाद से निपटने के लिए लगातार "पायथोनिक" की आवश्यकता होती है और वास्तव में पहले से ही threadingमॉड्यूल द्वारा पेश की जानी चाहिए - संभवतः सीधे मानक Threadवर्ग में। ThreadPoolसाधारण कार्यों के लिए बहुत अधिक उपरी रास्ता है - 3 प्रबंध सूत्र, नौकरशाही के बहुत सारे। दुर्भाग्य से Threadलेआउट को मूल रूप से जावा से कॉपी किया गया था - जिसे आप अभी भी बेकार 1 (!) कंस्ट्रक्टर पैरामीटर से देखते हैं group


पहला निर्माता बेकार नहीं है, भविष्य के कार्यान्वयन के लिए वहां आरक्षित है .. अजगर समानांतर प्रोग्रामिंग कुकबुक से
vijay टांगना

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))]
हेमंत एच कुमार

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

गाईसॉफ्ट का विचार बहुत अच्छा है, लेकिन मुझे लगता है कि ऑब्जेक्ट को थ्रेड से शुरू करने की आवश्यकता नहीं है और शुरू () इंटरफ़ेस से हटाया जा सकता है:

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 

यह अधिक उपयोगी है जहां आप यह खोजना चाहते हैं कि क्या किसी थ्रेड में से किसी ने आवश्यक कार्रवाई करने के लिए झूठी स्थिति वापस कर दी थी।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.