"আগুন এবং ভুলে যাও" পাইথন অ্যাসিঙ্ক / অপেক্ষা করুন


114

কখনও কখনও কিছু অ-সমালোচনামূলক অ্যাসিনক্রোনাস অপারেশন হয় যা হওয়ার দরকার হয় তবে আমি এটি শেষ হওয়ার জন্য অপেক্ষা করতে চাই না। টর্নেডোর কর্টিন প্রয়োগে আপনি yieldকী-শব্দটি বাদ দিয়ে কেবল একটি অ্যাসিঙ্ক্রোনাস ফাংশন "ফায়ার এবং ভুলে" যেতে পারেন ।

আমি পাইথন ৩.৩ এ প্রকাশিত নতুন 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 আসলে, আমি মনে করি এটি একটি সদৃশ, তবে আমি এটি তাত্ক্ষণিকভাবে-ডুপ-হাতুড়ি করতে চাই না। কেউ কি নিশ্চিত করতে পারবেন?
tobias_k

3
@ টোবিয়াস_ কে, আমি মনে করি এটি সদৃশ নয়। এই প্রশ্নের উত্তর হিসাবে লিঙ্কে উত্তর খুব বিস্তৃত।
মিখাইল গেরাসিমভ

2
(1) আপনার "মূল" প্রক্রিয়া চিরকালের জন্য চলতে থাকবে? অথবা (২) আপনি কি আপনার প্রক্রিয়াটি মরতে দিতে চান কিন্তু ভুলে যাওয়া কাজগুলিকে তাদের কাজ চালিয়ে যাওয়ার অনুমতি দিচ্ছেন? অথবা (3) আপনি কি আপনার মূল প্রক্রিয়াটি শেষ হওয়ার ঠিক আগে ভুলে যাওয়া কাজের জন্য অপেক্ষা করতে পছন্দ করেন?
জুলিয়েন প্যালার্ড

উত্তর:


168

Upd:

প্রতিস্থাপন asyncio.ensure_futureসঙ্গে asyncio.create_taskআপনি পাইথন> = 3.7 এটা নতুন আছে, সুন্দর পথ ব্যবহার করছেন সর্বত্র যদি ডিম কাজের জন্য


asyncio.Task "আগুন এবং ভুলে যাও"

পাইথন ডক্স অনুসারে "ব্যাকগ্রাউন্ডে" চালানোর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
@ গিল অ্যালেন এই বাক্য গঠনটি কেবল পাইথন 3.5+ তে কাজ করে। পাইথন ৩.৪-এর পুরানো বাক্য গঠন প্রয়োজন ( ডকস.পিথন.আর.৪.৪ / লাইব্রেরি / এসিনসিও- টাস্ক.চ.টি.এম.এল ) দেখুন। পাইথন ৩.৩ এবং এর নীচে অ্যাসিনসিও মোটেও সমর্থন করে না।
মিখাইল গেরাসিমভ

আপনি কীভাবে কোনও থ্রেডে কাজগুলি হত্যা করবেন?… ̣ আমার একটি থ্রেড রয়েছে যা কিছু কার্য তৈরি করে এবং থ্রেডটি stop()পদ্ধতিতে মারা যাওয়ার পরে আমি সমস্ত মুলতুবি থাকাগুলিকে হত্যা করতে চাই ।
সারদাথ্রিয়ন - এসই এর বিরুদ্ধে 15

@ সারদাথ্রিয়ন আমি নিশ্চিত নই যে টাস্কটি এটি তৈরি করা থ্রেডের দিকে কোথাও নির্দেশ করেছে, তবে কোনও কিছুই আপনাকে ম্যানুয়ালি ট্র্যাক করতে বাধা দেয় না: উদাহরণস্বরূপ, থ্রেডে তৈরি সমস্ত কাজকে একটি তালিকায় যুক্ত করুন এবং সময় আসার সাথে সাথে সেগুলি ব্যাখ্যা করার উপায়টি ব্যাখ্যা করুন time উপরে।
মিখাইল গেরাসিমভ

2
দ্রষ্টব্য যে "টাস্ক.আল_টাস্কস () পাইথন ৩.7 এর পরে অবহিত করা হয়েছে, পরিবর্তে অ্যাসিনসিও.ল_ টাস্কগুলি ব্যবহার করুন"
অ্যালেক্সিস

13

স্যাক্সি উত্তরের জন্য আপনাকে ধন্যবাদ সার্জি। এখানে একই সজ্জিত সংস্করণ।

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 ছোট অগ্নি-বিস্মৃত কার্য তৈরি করার পক্ষে যথেষ্ট গতি অনুভব করেছি। দীর্ঘমেয়াদী কাজের জন্য এটি উত্পাদনে ব্যবহার করবেন না। এটি আপনার সিপিইউ এবং মেমরিটি খাবে!
পীর

10

এটি সম্পূর্ণ অ্যাসিনক্রোনাস এক্সিকিউশন নয়, তবে সম্ভবত রান_ইন_এক্সেকিউটার () আপনার জন্য উপযুক্ত।

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 ছোট অগ্নি-বিস্মৃত কার্য তৈরি করার পরে আমি আপনার সতর্কতা এবং যথেষ্ট ধীরগতির দিকে খেয়াল রাখিনি। দীর্ঘমেয়াদী কাজের জন্য এটি উত্পাদনে ব্যবহার করবেন না। এটি আপনার সিপিইউ এবং মেমরিটি খাবে!
পীর

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

আমাদের যদি কেবল এই অগ্নি_আর_ফরজ কার্যকারিতা এবং অ্যাসিনসিও থেকে অন্য কিছু প্রয়োজন হয়, তবে কি অ্যাসিনসিও ব্যবহার করা ভাল? সুবিধা কি?
পীর
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.