"आग और भूल जाओ" अजगर async / प्रतीक्षा करें


115

कभी-कभी कुछ गैर-महत्वपूर्ण असिंक्रोनस ऑपरेशन होता है जिसे होने की आवश्यकता होती है लेकिन मैं इसे पूरा करने के लिए इंतजार नहीं करना चाहता। टॉरनेडो के कॉरटाइन कार्यान्वयन में आप yieldकुंजी शब्द को केवल ommitting द्वारा एक अतुल्यकालिक फ़ंक्शन को "आग और भूल सकते हैं" ।

मैं पायथन 3.5 में जारी किए गए नए async/ awaitवाक्यविन्यास के साथ "आग और भूल" कैसे पता लगाने की कोशिश कर रहा हूं । उदाहरण के लिए, एक सरल कोड स्निपेट:

async def async_foo():
    print("Do some stuff asynchronously here...")

def bar():
    async_foo()  # fire and forget "async_foo()"

bar()

हालांकि क्या होता है जो bar()कभी भी निष्पादित नहीं होता है और इसके बजाय हमें एक रनटाइम चेतावनी मिलती है:

RuntimeWarning: coroutine 'async_foo' was never awaited
  async_foo()  # fire and forget "async_foo()"

सम्बंधित? stackoverflow.com/q/32808893/1639625 वास्तव में, मुझे लगता है कि यह एक डुप्लिकेट है, लेकिन मैं इसे तुरंत-डुबो देना नहीं चाहता। क्या कोई पुष्टि कर सकता है?
तोबियास_

3
@tobias_k, मुझे नहीं लगता कि यह डुप्लिकेट है। इस प्रश्न के उत्तर के लिए लिंक पर उत्तर बहुत व्यापक है।
मिखाइल गेरासिमोव

2
क्या (1) आपकी "मुख्य" प्रक्रिया हमेशा चलती रहती है? या (2) क्या आप अपनी प्रक्रिया को मरने देना चाहते हैं लेकिन भूले हुए कार्यों को अनुमति देना उनकी नौकरी जारी है? या (3) क्या आप अपनी मुख्य प्रक्रिया को समाप्त होने से ठीक पहले भूल गए कार्यों की प्रतीक्षा कर रहे हैं?
जुलियन पालार्ड

जवाबों:


170

युपीडी:

बदलें asyncio.ensure_futureके साथ asyncio.create_taskआप अजगर> = 3.7 यह नए है, अच्छे तरह से उपयोग कर रहे हैं हर जगह है, तो अंडे कार्य करने के लिए


asyncio.Task to "आग और भूल"

अजगर डॉक्स के अनुसार "पृष्ठभूमि में"asyncio.Task को निष्पादित करने के लिए कुछ कॉरआउट शुरू करना संभव है । asyncio.ensure_future फ़ंक्शन द्वारा बनाया गया कार्य निष्पादन को अवरुद्ध नहीं करेगा (इसलिए फ़ंक्शन तुरंत वापस आ जाएगा!)। यह आपके अनुरोध के अनुसार "आग और भूलने" का एक तरीका है।

import asyncio


async def async_foo():
    print("async_foo started")
    await asyncio.sleep(1)
    print("async_foo done")


async def main():
    asyncio.ensure_future(async_foo())  # fire and forget async_foo()

    # btw, you can also create tasks inside non-async funcs

    print('Do some actions 1')
    await asyncio.sleep(1)
    print('Do some actions 2')
    await asyncio.sleep(1)
    print('Do some actions 3')


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

आउटपुट:

Do some actions 1
async_foo started
Do some actions 2
async_foo done
Do some actions 3

क्या होगा अगर इवेंट लूप पूरा होने के बाद कार्य निष्पादित हो रहे हैं?

ध्यान दें कि asyncio को उम्मीद है कि टास्क पूरा होने पर कार्य पूरा हो जाएगा। तो अगर आप बदलेंगे main():

async def main():
    asyncio.ensure_future(async_foo())  # fire and forget

    print('Do some actions 1')
    await asyncio.sleep(0.1)
    print('Do some actions 2')

कार्यक्रम समाप्त होने के बाद आपको यह चेतावनी मिलेगी:

Task was destroyed but it is pending!
task: <Task pending coro=<async_foo() running at [...]

यह रोकने के लिए कि आप इवेंट लूप पूरा होने के बाद सभी लंबित कार्यों का इंतजार कर सकते हैं :

async def main():
    asyncio.ensure_future(async_foo())  # fire and forget

    print('Do some actions 1')
    await asyncio.sleep(0.1)
    print('Do some actions 2')


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

    # Let's also finish all running tasks:
    pending = asyncio.Task.all_tasks()
    loop.run_until_complete(asyncio.gather(*pending))

उन्हें इंतजार करने के बजाय कार्यों को मार डालो

कभी-कभी आप किए जाने वाले कार्यों का इंतजार नहीं करना चाहते हैं (उदाहरण के लिए, कुछ कार्यों को हमेशा के लिए चलाने के लिए बनाया जा सकता है)। उस स्थिति में, आप उन्हें प्रतीक्षा करने के बजाय बस रद्द कर सकते हैं:

import asyncio
from contextlib import suppress


async def echo_forever():
    while True:
        print("echo")
        await asyncio.sleep(1)


async def main():
    asyncio.ensure_future(echo_forever())  # fire and forget

    print('Do some actions 1')
    await asyncio.sleep(1)
    print('Do some actions 2')
    await asyncio.sleep(1)
    print('Do some actions 3')


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

    # Let's also cancel all running tasks:
    pending = asyncio.Task.all_tasks()
    for task in pending:
        task.cancel()
        # Now we should await task to execute it's cancellation.
        # Cancelled task raises asyncio.CancelledError that we can suppress:
        with suppress(asyncio.CancelledError):
            loop.run_until_complete(task)

आउटपुट:

Do some actions 1
echo
Do some actions 2
echo
Do some actions 3
echo

मैंने पहले ब्लॉक को कॉपी और अतीत किया और बस इसे अपने अंत में चला दिया और किसी कारण से मुझे मिला: पंक्ति 4 async def async_foo (): ^ मानो पंक्ति 4 पर फ़ंक्शन परिभाषा के साथ कुछ सिंटैक्स त्रुटि है: "async def async_foo ( ):" क्या मैं कुछ भूल रहा हूँ?
गिल एलन

3
@GilAllen यह सिंटैक्स केवल पायथन 3.5+ में काम करता है। पायथन 3.4 को पुराने सिंटैक्स की आवश्यकता है (देखें docs.python.org/3.4/library/asyncio-task.html )। पायथन 3.3 और नीचे सभी में एसिंको का समर्थन नहीं करता है।
मिखाइल गेरेसिमोव

आप थ्रेड में कार्यों को कैसे मारेंगे? ... have मेरे पास एक धागा है जो कुछ कार्यों को बनाता है और मैं सभी लंबित लोगों को मारना चाहता हूं जब थ्रेड अपनी stop()विधि में मर जाता है ।
सारथ्रियन -

@Sardathrion मुझे यकीन नहीं है कि यदि कार्य थ्रेड पर कहीं इंगित करता है जिसमें इसे बनाया गया था, लेकिन कुछ भी आपको उन्हें मैन्युअल रूप से ट्रैक करने से नहीं रोकता है: उदाहरण के लिए, बस थ्रेड में बनाए गए सभी कार्यों को एक सूची में जोड़ें और जब समय आता है तो उन्हें रद्द करें ऊपर।
मिखाइल

2
ध्यान दें कि "Task.all_tasks () Python 3.7 के बाद से पदावनत हो गया है, इसके बजाय asyncio.all_tasks () का उपयोग करें"
एलेक्सिस

12

आप सर्गेई को रसीद जवाब के लिए धन्यवाद। यहाँ उसी का सजाया गया संस्करण है।

import asyncio
import time

def fire_and_forget(f):
    def wrapped(*args, **kwargs):
        return asyncio.get_event_loop().run_in_executor(None, f, *args, *kwargs)

    return wrapped

@fire_and_forget
def foo():
    time.sleep(1)
    print("foo() completed")

print("Hello")
foo()
print("I didn't wait for foo()")

का उत्पादन

>>> Hello
>>> foo() started
>>> I didn't wait for foo()
>>> foo() completed

नोट: मेरे अन्य उत्तर की जाँच करें जो सादे धागे का उपयोग करके ऐसा ही करता है।


मैं प्रति सेकंड ~ 5 छोटे आग और भूल कार्यों बनाने के इस दृष्टिकोण का उपयोग करने के बाद पर्याप्त मंदी का अनुभव किया। लंबे समय से चल रहे कार्य के लिए उत्पादन में इसका उपयोग न करें। यह आपके CPU और मेमोरी खा जाएगा!
पीर

10

यह पूरी तरह से अतुल्यकालिक निष्पादन नहीं है, लेकिन शायद run_in_executor () आपके लिए उपयुक्त है।

def fire_and_forget(task, *args, **kwargs):
    loop = asyncio.get_event_loop()
    if callable(task):
        return loop.run_in_executor(None, task, *args, **kwargs)
    else:    
        raise TypeError('Task must be a callable')

def foo():
    #asynchronous stuff here


fire_and_forget(foo)

3
अच्छा संक्षिप्त जवाब। यह ध्यान देने योग्य है कि executorकॉल करने के लिए डिफ़ॉल्ट होगा concurrent.futures.ThreadPoolExecutor.submit()। मैं उल्लेख करता हूं क्योंकि धागे बनाना स्वतंत्र नहीं है; आग-और-भूल 1000 बार एक सेकंड शायद धागा प्रबंधन पर एक बड़ा तनाव डाल देगा
ब्रैड सुलैमान

हां। मैंने आपकी चेतावनी पर ध्यान नहीं दिया और प्रति सेकंड ~ 5 छोटे फायर-एंड-फ़ोर टास्क बनाने वाले इस दृष्टिकोण का उपयोग करने के बाद पर्याप्त मंदी का अनुभव किया। लंबे समय से चल रहे कार्य के लिए उत्पादन में इसका उपयोग न करें। यह आपके CPU और मेमोरी खा जाएगा!
पीर

3

किसी कारण से यदि आप उपयोग करने में असमर्थ हैं asyncioतो यहां सादे धागे का उपयोग करके कार्यान्वयन किया जाता है। मेरे अन्य उत्तरों और सर्गेई के उत्तर की भी जाँच करें।

import threading

def fire_and_forget(f):
    def wrapped():
        threading.Thread(target=f).start()

    return wrapped

@fire_and_forget
def foo():
    time.sleep(1)
    print("foo() completed")

print("Hello")
foo()
print("I didn't wait for foo()")

अगर हमें केवल इस फायर_एंड_फॉरगेट फंक्शनलिटी की जरूरत है और एसिंको से और कुछ नहीं, तो क्या एसिंकोयो का इस्तेमाल करना बेहतर होगा? क्या है फायदे?
पीर
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.