पायथन में अतुल्यकालिक विधि कॉल?


178

मैं सोच रहा था कि पायथन में अतुल्यकालिक विधि कॉल के लिए कोई लाइब्रेरी है । यह बहुत अच्छा होगा यदि आप कुछ ऐसा कर सकते हैं

@async
def longComputation():
    <code>


token = longComputation()
token.registerCallback(callback_function)
# alternative, polling
while not token.finished():
    doSomethingElse()
    if token.finished():
        result = token.result()

या गैर-एस्किंक रूटीन को अतुल्यकालिक रूप से कॉल करने के लिए

def longComputation()
    <code>

token = asynccall(longComputation())

भाषा कोर में मूल के रूप में अधिक परिष्कृत रणनीति रखना बहुत अच्छा होगा। क्या इस पर विचार किया गया था?


पायथन 3.4 के रूप में: docs.python.org/3/library/asyncio.html (3.3 का बैकपोर्ट asyncऔर await3.5 से चमकदार नया और वाक्यविन्यास है)।
jonrsharpe

कॉलबैक तंत्र नहीं है, लेकिन आप एक शब्दकोश में परिणाम को एकत्र कर सकते हैं और यह पायथन के मल्टीप्रोसेसिंग मॉड्यूल पर आधारित है। मुझे यकीन है कि आप कॉलबैक के रूप में सजाए गए फ़ंक्शन में एक और पैरामीटर जोड़ सकते हैं। github.com/alex-sherman/deco
राजाविरवर्मा

आरंभ करना। आधिकारिक दस्तावेज - docs.python.org/3/library/concurrency.html
आदर्श मादरेचा

जवाबों:


141

आप पायथन 2.6 में जोड़े गए मल्टीप्रोसेसिंग मॉड्यूल का उपयोग कर सकते हैं । आप प्रक्रियाओं के पूल का उपयोग कर सकते हैं और फिर परिणाम को असंगत रूप से प्राप्त कर सकते हैं:

apply_async(func[, args[, kwds[, callback]]])

उदाहरण के लिए:

from multiprocessing import Pool

def f(x):
    return x*x

if __name__ == '__main__':
    pool = Pool(processes=1)              # Start a worker processes.
    result = pool.apply_async(f, [10], callback) # Evaluate "f(10)" asynchronously calling callback when finished.

यह केवल एक ही विकल्प है। यह मॉड्यूल जो आप चाहते हैं उसे प्राप्त करने के लिए बहुत सारी सुविधाएं प्रदान करता है। साथ ही इससे डेकोरेटर बनाना वाकई आसान होगा।


5
लुकास एस।, आपका उदाहरण काम नहीं करता है, दुर्भाग्य से। कॉलबैक फ़ंक्शन को कभी कॉल नहीं किया जाता है।
डेटाग्रेड

6
यह संभवतः ध्यान में रखने योग्य है कि यह एक प्रक्रिया के भीतर अलग-अलग धागे के बजाय अलग-अलग प्रक्रियाएं पैदा करता है। इसके कुछ निहितार्थ हो सकते हैं।
user47741

11
यह काम करता है: परिणाम = pool.apply_async (f, [10], कॉलबैक = फिनिश)
MJ

6
वास्तव में अजगर में अतुल्यकालिक रूप से कुछ भी करने के लिए नई प्रक्रियाओं को स्पॉन करने के लिए मल्टीप्रोसेसिंग मॉड्यूल का उपयोग करना पड़ता है। नए थ्रेड बनाने का काम अभी भी ग्लोबल इंटरप्रेटर लॉक की दया पर है जो एक अजगर प्रक्रिया को एक साथ कई काम करने से रोकता है।
दारकर

2
यदि आप इस समाधान का उपयोग करते समय कोई नई प्रक्रिया नहीं चाहते हैं - तो आयात को बदल दें from multiprocessing.dummy import Pool। मल्टीप्रोसेसिंग।
डमी

203

कुछ इस तरह:

import threading

thr = threading.Thread(target=foo, args=(), kwargs={})
thr.start() # Will run "foo"
....
thr.is_alive() # Will return whether foo is running currently
....
thr.join() # Will wait till "foo" is done

अधिक विवरण के लिए दस्तावेज़ीकरण https://docs.python.org/library/threading.html पर देखें।


1
हाँ, अगर आपको सिर्फ अतुल्यकालिक चीजें करने की ज़रूरत है, तो बस थ्रेड का उपयोग क्यों न करें? सभी थ्रेड प्रक्रिया के बाद हल्के वजन के होते हैं
kk1957

22
महत्वपूर्ण नोट: थ्रेड्स के मानक कार्यान्वयन (CPython) "ग्लोबल इंटरप्रेटर लॉक" के कारण, कम्प्यूट-बाउंड कार्यों के साथ मदद नहीं करेगा। लाइब्रेरी डॉक देखें: लिंक
घुलनशील मछली

3
क्या वास्तव में अतुल्यकालिक है। क्या होगा यदि आप एक थ्रेड को ब्लॉक नहीं करना चाहते हैं (जैसे कि यूआई थ्रेड) और उस पर लूप करते समय एक टन संसाधनों का उपयोग न करें?
मगमर्ज़

1
@ ममगेरेज़ ज्वाइन समकालिक है। आप थ्रेड को कुछ कतार में निष्पादन के परिणामों को रखने के लिए दे सकते हैं, या / और कॉलबैक कह सकते हैं। अन्यथा आप नहीं जानते कि यह कब किया गया है (यदि बिल्कुल भी)।
२०:१० पर द्राकोशा

1
क्या थ्रेड निष्पादन के अंत में कॉलबैक फ़ंक्शन को कॉल करना संभव है जैसे आप मल्टीप्रोसेसिंग के साथ कर सकते हैं। मूल
Reda Drissi

49

Python 3.5 के रूप में, आप async फ़ंक्शन के लिए एन्हांस्ड जनरेटर का उपयोग कर सकते हैं।

import asyncio
import datetime

संवर्धित जनरेटर सिंटैक्स:

@asyncio.coroutine
def display_date(loop):
    end_time = loop.time() + 5.0
    while True:
        print(datetime.datetime.now())
        if (loop.time() + 1.0) >= end_time:
            break
        yield from asyncio.sleep(1)


loop = asyncio.get_event_loop()
# Blocking call which returns when the display_date() coroutine is done
loop.run_until_complete(display_date(loop))
loop.close()

नया async/awaitवाक्यविन्यास:

async def display_date(loop):
    end_time = loop.time() + 5.0
    while True:
        print(datetime.datetime.now())
        if (loop.time() + 1.0) >= end_time:
            break
        await asyncio.sleep(1)


loop = asyncio.get_event_loop()
# Blocking call which returns when the display_date() coroutine is done
loop.run_until_complete(display_date(loop))
loop.close()

8
@ कर्णबेह, क्या आप ओपी के "डीप लॉन्ग कॉमप्यूटेशन ()" फंक्शन को शामिल करने के लिए उस उदाहरण का विस्तार कर सकते हैं? अधिकांश उदाहरण "प्रतीक्षा asyncio.sleep (1)" का उपयोग करते हैं, लेकिन यदि longComputation () रिटर्न, कहते हैं, एक डबल, तो आप बस "प्रतीक्षा longComputation ()" का उपयोग नहीं कर सकते हैं।
फैब

भविष्य में दस साल और यह अब स्वीकार किया जाना चाहिए। जब आप python3.5 में async के बारे में बात करते हैं + तो मन में जो आता है वह asyncio और async कीवर्ड होना चाहिए।
जेह

31

यह भाषा के मूल में नहीं है, लेकिन एक बहुत ही परिपक्व पुस्तकालय है जो आपको चाहिए वह है मुड़ । यह आस्थगित वस्तु का परिचय देता है, जिसे आप कॉलबैक या त्रुटि हैंडलर ("गलतियां") को संलग्न कर सकते हैं। एक आस्थगित मूल रूप से एक "वादा" है कि एक फ़ंक्शन का परिणाम अंततः होगा।


1
विशेष रूप से, twisted.internet.defer ( twistedmatrix.com/documents/8.2.0/api/… ) देखें।
निकोलस रिले

21

आप अपने कार्यों को अतुल्यकालिक बनाने के लिए एक डेकोरेटर को लागू कर सकते हैं, हालांकि यह थोड़ा मुश्किल है। multiprocessingमॉड्यूल थोड़ा quirks और उचित रूप में मनमाने प्रतिबन्ध से भरा है - सभी को और अधिक है, हालांकि एक अनुकूल इंटरफेस के पीछे यह संपुटित करने के लिए कारण।

from inspect import getmodule
from multiprocessing import Pool


def async(decorated):
    r'''Wraps a top-level function around an asynchronous dispatcher.

        when the decorated function is called, a task is submitted to a
        process pool, and a future object is returned, providing access to an
        eventual return value.

        The future object has a blocking get() method to access the task
        result: it will return immediately if the job is already done, or block
        until it completes.

        This decorator won't work on methods, due to limitations in Python's
        pickling machinery (in principle methods could be made pickleable, but
        good luck on that).
    '''
    # Keeps the original function visible from the module global namespace,
    # under a name consistent to its __name__ attribute. This is necessary for
    # the multiprocessing pickling machinery to work properly.
    module = getmodule(decorated)
    decorated.__name__ += '_original'
    setattr(module, decorated.__name__, decorated)

    def send(*args, **opts):
        return async.pool.apply_async(decorated, args, opts)

    return send

नीचे दिया गया कोड डेकोरेटर के उपयोग को दर्शाता है:

@async
def printsum(uid, values):
    summed = 0
    for value in values:
        summed += value

    print("Worker %i: sum value is %i" % (uid, summed))

    return (uid, summed)


if __name__ == '__main__':
    from random import sample

    # The process pool must be created inside __main__.
    async.pool = Pool(4)

    p = range(0, 1000)
    results = []
    for i in range(4):
        result = printsum(i, sample(p, 100))
        results.append(result)

    for result in results:
        print("Worker %i: sum value is %i" % result.get())

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


यह सबसे अच्छा जवाब होना चाहिए। मैं प्यार करता हूँ कि यह मूल्य कैसे लौटा सकता है। उस धागे की तरह नहीं जो केवल अतुल्यकालिक रूप से चल रहा है।
अमीना नुरैनी

16

केवल

import threading, time

def f():
    print "f started"
    time.sleep(3)
    print "f finished"

threading.Thread(target=f).start()

8

आप ईवेंटलेट का उपयोग कर सकते हैं। यह आपको यह लिखता है कि समकालिक कोड क्या प्रतीत होता है, लेकिन क्या यह नेटवर्क पर अतुल्यकालिक रूप से संचालित होता है।

यहाँ एक सुपर न्यूनतम क्रॉलर का उदाहरण दिया गया है:

urls = ["http://www.google.com/intl/en_ALL/images/logo.gif",
     "https://wiki.secondlife.com/w/images/secondlife.jpg",
     "http://us.i1.yimg.com/us.yimg.com/i/ww/beta/y3.gif"]

import eventlet
from eventlet.green import urllib2

def fetch(url):

  return urllib2.urlopen(url).read()

pool = eventlet.GreenPool()

for body in pool.imap(fetch, urls):
  print "got body", len(body)

7

मेरा समाधान है:

import threading

class TimeoutError(RuntimeError):
    pass

class AsyncCall(object):
    def __init__(self, fnc, callback = None):
        self.Callable = fnc
        self.Callback = callback

    def __call__(self, *args, **kwargs):
        self.Thread = threading.Thread(target = self.run, name = self.Callable.__name__, args = args, kwargs = kwargs)
        self.Thread.start()
        return self

    def wait(self, timeout = None):
        self.Thread.join(timeout)
        if self.Thread.isAlive():
            raise TimeoutError()
        else:
            return self.Result

    def run(self, *args, **kwargs):
        self.Result = self.Callable(*args, **kwargs)
        if self.Callback:
            self.Callback(self.Result)

class AsyncMethod(object):
    def __init__(self, fnc, callback=None):
        self.Callable = fnc
        self.Callback = callback

    def __call__(self, *args, **kwargs):
        return AsyncCall(self.Callable, self.Callback)(*args, **kwargs)

def Async(fnc = None, callback = None):
    if fnc == None:
        def AddAsyncCallback(fnc):
            return AsyncMethod(fnc, callback)
        return AddAsyncCallback
    else:
        return AsyncMethod(fnc, callback)

और बिल्कुल अनुरोध के अनुसार काम करता है:

@Async
def fnc():
    pass

5

मेरे लिए कुछ इस तरह काम करता है, तो आप फ़ंक्शन को कॉल कर सकते हैं, और यह खुद को एक नए थ्रेड पर भेज देगा।

from thread import start_new_thread

def dowork(asynchronous=True):
    if asynchronous:
        args = (False)
        start_new_thread(dowork,args) #Call itself on a new thread.
    else:
        while True:
            #do something...
            time.sleep(60) #sleep for a minute
    return

2

क्या थ्रेड्स का उपयोग न करने का कोई कारण है? आप threadingवर्ग का उपयोग कर सकते हैं । finished()फ़ंक्शन के बजाय का उपयोग करें isAlive()result()समारोह सकता है join()धागा और परिणाम पुनः प्राप्त। और, यदि आप कंस्ट्रक्टर में निर्दिष्ट फ़ंक्शन को कॉल करने के लिए run()और __init__फ़ंक्शन को ओवरराइड कर सकते हैं, और क्लास के उदाहरण में कहीं मान को सहेज सकते हैं।


2
यदि यह कम्प्यूटेशनल रूप से महंगा फ़ंक्शन थ्रेडिंग है, तो आपको कुछ भी नहीं मिलेगा (यह संभवतः चीजों को वास्तव में धीमा कर देगा) क्योंकि जीआईआईएल के कारण एक पायथन प्रक्रिया एक सीपीयू कोर तक सीमित है।
कर्ट

2
@ कर्ट, जबकि यह सच है, ओपी ने उल्लेख नहीं किया कि प्रदर्शन उनकी चिंता थी। अतुल्यकालिक व्यवहार के चाहने के अन्य कारण हैं ...
पीटर हैनसेन

अजगर में धागे महान नहीं हैं जब आप अतुल्यकालिक विधि कॉल को मारने का विकल्प चाहते हैं, क्योंकि केवल अजगर में मुख्य धागा संकेत प्राप्त करता है।
CivFan

2

आप समवर्ती का उपयोग कर सकते हैं। फ़्यूचर (पायथन 3.2 में जोड़ा गया)।

import time
from concurrent.futures import ThreadPoolExecutor


def long_computation(duration):
    for x in range(0, duration):
        print(x)
        time.sleep(1)
    return duration * 2


print('Use polling')
with ThreadPoolExecutor(max_workers=1) as executor:
    future = executor.submit(long_computation, 5)
    while not future.done():
        print('waiting...')
        time.sleep(0.5)

    print(future.result())

print('Use callback')
executor = ThreadPoolExecutor(max_workers=1)
future = executor.submit(long_computation, 5)
future.add_done_callback(lambda f: print(f.result()))

print('waiting for callback')

executor.shutdown(False)  # non-blocking

print('shutdown invoked')

यह एक बहुत ही शानदार जवाब है, क्योंकि यह एकमात्र ऐसा है जो कॉलबैक के साथ एक थ्रेडपुल की संभावना देता है
Reda Drissi

दुर्भाग्य से, यह "ग्लोबल इंटरप्रेटर लॉक" से भी ग्रस्त है। लाइब्रेरी डॉक देखें: लिंक । पायथन 3.7 के साथ परीक्षण किया गया
एलेक्स

0

आप प्रक्रिया का उपयोग कर सकते हैं। यदि आप इसे हमेशा के लिए उपयोग करना चाहते हैं (जैसे नेटवर्किंग) तो आप कार्य करते हैं:

from multiprocessing import Process
def foo():
    while 1:
        # Do something

p = Process(target = foo)
p.start()

यदि आप इसे एक बार चलाना चाहते हैं, तो ऐसा करें:

from multiprocessing import Process
def foo():
    # Do something

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