asyncio.ensure_future बनाम BaseEventLoop.create_task बनाम सरल कोरआउट?


97

मैंने कई मूल पायथन 3.5 ट्यूटोरियल देखे हैं, जो विभिन्न स्वादों में समान संचालन कर रहे हैं। इस कोड में:

import asyncio  

async def doit(i):
    print("Start %d" % i)
    await asyncio.sleep(3)
    print("End %d" % i)
    return i

if __name__ == '__main__':
    loop = asyncio.get_event_loop()
    #futures = [asyncio.ensure_future(doit(i), loop=loop) for i in range(10)]
    #futures = [loop.create_task(doit(i)) for i in range(10)]
    futures = [doit(i) for i in range(10)]
    result = loop.run_until_complete(asyncio.gather(*futures))
    print(result)

उपरोक्त सभी तीन वेरिएंट जो futuresवेरिएबल को परिभाषित करते हैं, वही परिणाम प्राप्त करते हैं; एकमात्र अंतर जो मैं देख सकता हूं वह यह है कि तीसरे संस्करण के साथ निष्पादन आदेश से बाहर है (जो ज्यादातर मामलों में मायने नहीं रखना चाहिए)। क्या कोई और अंतर है? क्या ऐसे मामले हैं जहां मैं केवल सरल संस्करण (कोरटाइन की सादे सूची) का उपयोग नहीं कर सकता हूं?

जवाबों:


118

वास्तविक जानकारी:

पायथन 3.7 से शुरू करके इस उद्देश्य के लिए asyncio.create_task(coro)उच्च-स्तरीय समारोह जोड़ा गया

आपको इसका उपयोग कोरटाइम्स से कार्य बनाने के अन्य तरीकों के बजाय करना चाहिए। हालाँकि यदि आपको मनमाना प्रतीक्षा से कार्य बनाने की आवश्यकता है, तो आपको उपयोग करना चाहिए asyncio.ensure_future(obj)


पुरानी जानकारी:

ensure_future बनाम create_task

ensure_futureबनाने के लिए एक विधि है Taskसे coroutine। यह तर्क के आधार पर अलग-अलग तरीकों से कार्य बनाता है ( create_taskकोरटाइन और भविष्य जैसी वस्तुओं के उपयोग के लिए)।

create_taskकी एक अमूर्त विधि है AbstractEventLoop। अलग-अलग इवेंट लूप इस फ़ंक्शन को अलग-अलग तरीकों से लागू कर सकते हैं।

आपको ensure_futureकार्य बनाने के लिए उपयोग करना चाहिए । आपको create_taskकेवल तभी आवश्यकता होगी जब आप अपना स्वयं का ईवेंट लूप प्रकार लागू करने जा रहे हों।

युपीडी:

@ bj0 ने इस विषय पर गुइडो के उत्तर की ओर इशारा किया :

इसका मतलब यह है ensure_future()कि यदि आपके पास कुछ ऐसा है जो या तो एक कोरटाइन हो सकता है या Future(उत्तरार्द्ध में एक Taskउप-वर्ग शामिल है क्योंकि Future), और आप उस पर एक विधि को कॉल करने में सक्षम होना चाहते हैं जो केवल परिभाषित है Future(शायद केवल के बारे में) उपयोगी उदाहरण cancel())। जब यह पहले से ही है Future(या Task) यह कुछ नहीं करता है; जब यह एक coroutine होता है तो इसे एक में लपेट देता है Task

यदि आप जानते हैं कि आपके पास एक कॉरआउट है और आप इसे शेड्यूल करना चाहते हैं, तो उपयोग करने के लिए सही एपीआई है create_task()। केवल तभी जब आपको कॉल किया जाना चाहिए ensure_future(), जब आप एक एपीआई प्रदान कर रहे हों (जैसे कि एसिंनसियो के अपने एपीआई में से अधिकांश) जो या तो एक कॉरआउट या एक को स्वीकार करता है Futureऔर आपको इसके लिए कुछ करने की आवश्यकता होती है जिसके लिए आपको एक होना चाहिए Future

और बादमें:

अंत में मुझे अभी भी विश्वास है कि ensure_future()कार्यक्षमता के शायद ही कभी आवश्यक टुकड़े के लिए एक उचित अस्पष्ट नाम है। एक कोरटाइन से कार्य बनाते समय आपको उचित रूप से नामित का उपयोग करना चाहिए loop.create_task()। हो सकता है कि इसके लिए एक उपनाम होना चाहिए asyncio.create_task()?

यह मेरे लिए आश्चर्य की बात है। ensure_futureसभी के साथ उपयोग करने के लिए मेरी मुख्य प्रेरणा यह थी कि यह लूप के सदस्य की तुलना में उच्च-स्तरीय फ़ंक्शन है create_task(चर्चा में कुछ विचार शामिल हैं जैसे जोड़ना asyncio.spawnया asyncio.create_task)।

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

हालांकि, गुइडो का जवाब स्पष्ट है: "जब एक कोरटाइन से एक कार्य बनाते हैं तो आपको उचित रूप से नामित का उपयोग करना चाहिए loop.create_task()"

जब कोरटाइन को कार्यों में लपेटा जाना चाहिए?

एक टास्क में कॉरपेट लपेटें - इस कॉरआउट को "पृष्ठभूमि में" शुरू करने का एक तरीका है। यहाँ उदाहरण है:

import asyncio


async def msg(text):
    await asyncio.sleep(0.1)
    print(text)


async def long_operation():
    print('long_operation started')
    await asyncio.sleep(3)
    print('long_operation finished')


async def main():
    await msg('first')

    # Now you want to start long_operation, but you don't want to wait it finised:
    # long_operation should be started, but second msg should be printed immediately.
    # Create task to do so:
    task = asyncio.ensure_future(long_operation())

    await msg('second')

    # Now, when you want, you can await task finised:
    await task


if __name__ == "__main__":
    loop = asyncio.get_event_loop()
    loop.run_until_complete(main())

आउटपुट:

first
long_operation started
second
long_operation finished

आप asyncio.ensure_future(long_operation())केवल await long_operation()अंतर महसूस करने के लिए बदल सकते हैं ।


3
गुइडो के अनुसार, create_taskयदि आपको वास्तव में

@ bj0 इस लिंक के लिए धन्यवाद। मैंने इस चर्चा से जानकारी जोड़ने का उत्तर अपडेट किया।
मिखाइल गेरेसिमोव

करता है ensure_futureस्वचालित रूप से बनाया जोड़ता Taskमुख्य समारोह पाश के लिए?
AlQuemist

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

2
@laycat हमें दूसरी कॉल पर ईवेंट लूप पर नियंत्रण वापस करने की आवश्यकता awaitहै msg()। एक बार नियंत्रण प्राप्त करने वाले इवेंट लूप शुरू करने में सक्षम होंगे long_operation()। इसने प्रदर्शित किया कि ensure_futureवर्तमान निष्पादन प्रवाह के साथ समवर्ती रूप से निष्पादित करने के लिए कोरटाइन कैसे शुरू होता है।
मिखाइल गेरेसिमोव

45

create_task()

  • धनिया स्वीकार करता है,
  • रिटर्न टास्क,
  • लूप के संदर्भ में इसे लागू किया जाता है।

ensure_future()

  • वायदा, कोरटाइन, प्रतीक्षा करने योग्य वस्तुओं को स्वीकार करता है,
  • टास्क देता है (या फ्यूचर अगर फ्यूचर पास हो गया है)।
  • यदि दिए गए arg एक coroutine है जिसका उपयोग करता है create_task,
  • लूप ऑब्जेक्ट को पारित किया जा सकता है।

जैसा कि आप देख सकते हैं create_task अधिक विशिष्ट है।


async create_task या सुनिश्चित_फॉवेल के बिना फ़ंक्शन

सरल इनवॉइसिंग asyncफ़ंक्शन कॉरआउट करता है

>>> async def doit(i):
...     await asyncio.sleep(3)
...     return i
>>> doit(4)   
<coroutine object doit at 0x7f91e8e80ba0>

और चूंकि gatherहुड सुनिश्चित करता है ( ensure_future) कि आर्ग्स वायदा हैं, स्पष्ट रूप सेensure_future से बेमानी है।

इसी तरह के सवाल loop.create_task, asyncio.async / सुनिश्चित_फंच और टास्क के बीच अंतर क्या है?


13

नोट: केवल Python 3.7 के लिए मान्य है (Python 3.5 के लिए पहले के उत्तर को देखें )।

आधिकारिक डॉक्स से:

asyncio.create_task(पायथन 3.7 में जोड़ा गया) के बजाय नए कार्यों को पैदा करने के लिए बेहतर तरीका है ensure_future()


विवरण:

तो अब, पायथन 3.7 में आगे, 2 शीर्ष-स्तरीय आवरण फ़ंक्शन (समान लेकिन अलग) हैं:

ठीक है, पूरी तरह से इन दोनों आवरण कार्यों से आपको कॉल करने में मदद मिलेगी BaseEventLoop.create_task। एकमात्र अंतर ensure_futureकिसी भी awaitableवस्तु को स्वीकार करने और इसे भविष्य में परिवर्तित करने में आपकी सहायता करता है। और आप अपने खुद के event_loopपैरामीटर भी प्रदान कर सकते हैं ensure_future। और अगर आपको उन क्षमताओं की आवश्यकता है या नहीं, तो आप बस यह चुन सकते हैं कि किस आवरण का उपयोग करना है।


मुझे लगता है कि एक और अंतर है जो प्रलेखित नहीं है: यदि आप लूप को चलाने से पहले asyncio.create_task को कॉल करने का प्रयास करते हैं, तो आपको एक समस्या होगी क्योंकि asyncio.create_task एक रनिंग लूप की उम्मीद कर रहा है। आप इस मामले में asyncio.ensure_future का उपयोग कर सकते हैं, हालांकि, चूंकि रनिंग लूप कोई आवश्यकता नहीं है।
coelhudo

4

आपके उदाहरण के लिए, सभी तीन प्रकार अतुल्यकालिक रूप से निष्पादित करते हैं। एकमात्र अंतर यह है कि, तीसरे उदाहरण में, आपने सभी 10 कोरआउट को पूर्व-उत्पन्न किया, और एक साथ लूप में जमा किया। तो केवल आखिरी एक आउटपुट बेतरतीब ढंग से देता है।

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