পাইথন 3.5 তে কর্টিন এবং ভবিষ্যতের / টাস্কের মধ্যে পার্থক্য?


102

ধরা যাক আমাদের একটি ডামি ফাংশন রয়েছে:

async def foo(arg):
    result = await some_remote_call(arg)
    return result.upper()

এর মধ্যে পার্থক্য কী:

import asyncio    

coros = []
for i in range(5):
    coros.append(foo(i))

loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(coros))

এবং:

import asyncio

futures = []
for i in range(5):
    futures.append(asyncio.ensure_future(foo(i)))

loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(futures))

দ্রষ্টব্য : উদাহরণটি ফলাফল দেয় তবে এটি প্রশ্নের কেন্দ্রবিন্দু নয়। যখন রিটার্ন মান সম্পর্কিত হয়, gather()পরিবর্তে ব্যবহার করুন wait()

রিটার্ন মান নির্বিশেষে, আমি স্পষ্টতা খুঁজছি ensure_future()wait(coros)এবং wait(futures)উভয়ই কর্টিনগুলি চালায়, সুতরাং কখন এবং কেন কোনও কর্টিন মোড়ানো উচিত ensure_future?

মূলত, পাইথন 3.5 ব্যবহার করে একগুচ্ছ নন-ব্লকিং অপারেশন চালানোর সঠিক উপায় (টিএম) কী async?

অতিরিক্ত creditণের জন্য, যদি আমি কলগুলি ব্যাচ করতে চাই? উদাহরণস্বরূপ, আমাকে some_remote_call(...)1000 বার কল করতে হবে , তবে আমি 1000 একযোগে সংযোগের মাধ্যমে ওয়েব সার্ভার / ডাটাবেস / ইত্যাদি ক্রাশ করতে চাই না। এটি থ্রেড বা প্রক্রিয়া পুলের সাহায্যে করণীয় তবে এটি করার কোনও উপায় আছে কি asyncio?

2020 আপডেট (পাইথন 3.7+) : এই স্নিপেটগুলি ব্যবহার করবেন না। পরিবর্তে ব্যবহার করুন:

import asyncio

async def do_something_async():
    tasks = []
    for i in range(5):
        tasks.append(asyncio.create_task(foo(i)))
    await asyncio.gather(*tasks)

def do_something():
    asyncio.run(do_something_async)

এছাড়াও ব্যবহারের বিষয়ে বিবেচনা ট্রিও , asyncio করার জন্য একটি শক্তসমর্থ 3rd পার্টি বিকল্প।

উত্তর:


97

একটি কর্টাইন একটি জেনারেটর ফাংশন যা উভয়ই মূল্য দিতে পারে এবং বাইরে থেকে মান গ্রহণ করতে পারে। কর্টিন ব্যবহারের সুবিধাটি হ'ল আমরা কোনও ফাংশন কার্যকর করার জন্য বিরতি দিতে পারি এবং পরে এটি পুনরায় শুরু করতে পারি। কোনও নেটওয়ার্ক অপারেশনের ক্ষেত্রে, আমরা প্রতিক্রিয়ার জন্য অপেক্ষা করতে থাকাকালীন কোনও ক্রিয়াকলাপের কার্যকারিতাটি বিরতি দেওয়ার অর্থটি বোধ হয়। আমরা সময়টি অন্য কয়েকটি ফাংশন চালাতে ব্যবহার করতে পারি।

ভবিষ্যত Promiseজাভাস্ক্রিপ্ট থেকে প্রাপ্ত বস্তুর মতো । এটি এমন কোনও মানের জন্য স্থানধারীর মতো যা ভবিষ্যতে বাস্তবায়িত হবে। উল্লিখিত ক্ষেত্রে, নেটওয়ার্ক আই / ও-তে অপেক্ষা করার সময়, একটি ফাংশন আমাদের একটি ধারক দিতে পারে, একটি প্রতিশ্রুতি দেয় যে এটি অপারেশন শেষ হলে মানটি দিয়ে ধারকটি পূরণ করবে। আমরা ভবিষ্যতের বিষয়টিকে ধরে রাখি এবং এটি শেষ হয়ে গেলে, আসল ফলাফলটি পুনরুদ্ধার করার জন্য আমরা এটিতে একটি পদ্ধতি কল করতে পারি।

সরাসরি উত্তরঃ আপনি প্রয়োজন হবে না ensure_futureযদি আপনি ফলাফল প্রয়োজন হবে না। আপনার যদি ফলাফলের প্রয়োজন হয় বা ব্যতিক্রমগুলি পুনরুদ্ধার করা হয় তবে সেগুলি ভাল।

অতিরিক্ত ক্রেডিট: আমি সর্বাধিক কর্মীদের সংখ্যা নিয়ন্ত্রণ করতে run_in_executorএকটি Executorউদাহরণ নির্বাচন করব এবং পাস করব would

ব্যাখ্যা এবং নমুনা কোড

প্রথম উদাহরণে, আপনি কর্টিন ব্যবহার করছেন। waitফাংশন coroutines একটি গুচ্ছ নেয় এবং তাদের একসঙ্গে সম্মিলন। সুতরাং wait()সমস্ত কর্টিনগুলি ক্লান্ত হয়ে গেলে (সমস্ত মানগুলি সমাপ্ত / সমাপ্ত)।

loop = get_event_loop() # 
loop.run_until_complete(wait(coros))

run_until_completeপদ্ধতি নিশ্চিত করুন যে লুপ পর্যন্ত ফাঁসি সমাপ্ত হয় জীবিত করতে হবে। আপনি কীভাবে এই ক্ষেত্রে অ্যাসিঙ্ক কার্যকর করার ফলাফল পাচ্ছেন না তা দয়া করে লক্ষ্য করুন।

দ্বিতীয় উদাহরণে, আপনি ensure_futureকোনও করোটিন মোড়ানোর জন্য এবং Taskকোনও ধরণের যাবতীয় জিনিসটি ফেরত দেওয়ার জন্য ফাংশনটি ব্যবহার করছেন Future। আপনি যখন কল করবেন তখন কর্টিনটি মূল ইভেন্ট লুপে নির্বাহ করা হবে ensure_future। প্রত্যাবর্তিত ভবিষ্যত / টাস্ক অবজেক্টটির এখনও কোনও মূল্য নেই তবে সময়ের সাথে সাথে, যখন নেটওয়ার্ক অপারেশনগুলি সমাপ্ত হবে, ভবিষ্যতের অবজেক্টটি অপারেশনের ফলাফলকে ধরে রাখবে।

from asyncio import ensure_future

futures = []
for i in range(5):
    futures.append(ensure_future(foo(i)))

loop = get_event_loop()
loop.run_until_complete(wait(futures))

সুতরাং এই উদাহরণে, আমরা একই জিনিস করছি কেবলমাত্র আমরা কর্টিন ব্যবহার না করে ফিউচার ব্যবহার করছি।

আসিনেসিও / কর্টিন / ফিউচার কীভাবে ব্যবহার করবেন তার উদাহরণ দেখুন:

import asyncio


async def slow_operation():
    await asyncio.sleep(1)
    return 'Future is done!'


def got_result(future):
    print(future.result())

    # We have result, so let's stop
    loop.stop()


loop = asyncio.get_event_loop()
task = loop.create_task(slow_operation())
task.add_done_callback(got_result)

# We run forever
loop.run_forever()

এখানে আমরা অবজেক্টে create_taskমেথড ব্যবহার করেছি loopensure_futureমূল ইভেন্ট লুপ এ কাজ শিডিউল হবে। এই পদ্ধতিটি আমাদের চয়ন করে একটি লুপে একটি কর্টিন নির্ধারণ করতে সক্ষম করে।

আমরা add_done_callbackটাস্ক অবজেক্টে পদ্ধতিটি ব্যবহার করে কলব্যাক যুক্ত করার ধারণাটিও দেখতে পাই ।

Taskহল doneযখন কর্টাইন কোনও মান ফেরত দেয়, একটি ব্যতিক্রম উত্থাপন করে বা বাতিল হয়ে যায়। এই ঘটনাগুলি পরীক্ষা করার জন্য বিভিন্ন পদ্ধতি রয়েছে।

আমি এই বিষয়গুলিতে কিছু ব্লগ পোস্ট লিখেছি যা সহায়তা করতে পারে:

অবশ্যই, আপনি সরকারী ম্যানুয়ালটিতে আরও বিশদ জানতে পারবেন: https://docs.python.org/3/library/asyncio.html


4
আমি আমার প্রশ্নটি আরও স্পষ্ট করে আপডেট করেছি - যদি আমার কর্টিনের থেকে ফলাফলের প্রয়োজন না হয় তবে আমার কী এখনও ব্যবহার করা দরকার ensure_future()? এবং যদি আমার ফলাফলের প্রয়োজন হয়, আমি কি কেবল ব্যবহার করতে পারি না run_until_complete(gather(coros))?
নিট 21

4
ensure_futureইভেন্ট লুপে সঞ্চালিত কর্টিনকে সময়সূচি দেয়। সুতরাং আমি হ্যাঁ বলব, এটি প্রয়োজন। তবে অবশ্যই আপনি অন্যান্য ফাংশন / পদ্ধতিগুলি ব্যবহার করে কর্টিনগুলি শিডিউল করতে পারেন। হ্যাঁ, আপনি ব্যবহার করতে পারেন gather()- তবে জড়ো হওয়া সমস্ত প্রতিক্রিয়া সংগ্রহ না করা অবধি অপেক্ষা করবে।
মাসনুন

4
@ আবুআশরাফমাসনুন @ কুনাইট gatherএবং waitপ্রকৃতপক্ষে প্রদত্ত করোটিনগুলিকে কাজে ব্যবহার করে মোড়ানো করুন ensure_future(উত্সগুলি এখানে এবং এখানে দেখুন )। সুতরাং ensure_futureআগে ব্যবহার করার কোনও অর্থ নেই এবং ফলাফল পাওয়ার বা না পাওয়ার সাথে এর কোনও যোগসূত্র নেই।
ভিনসেন্ট

8
@AbuAshrafMasnun এছাড়াও @knite, ensure_futureএকটি আছে loopযুক্তি, তাই ব্যবহার করার কোনও কারণ নেই loop.create_taskওভার ensure_future। এবং run_in_executorকরোটিনগুলির সাথে কাজ করবে না, পরিবর্তে একটি সেমফোর ব্যবহার করা উচিত।
ভিনসেন্ট

4
@vincent সেখানে ব্যবহার করার জন্য একটি কারণ নেই create_taskওভার ensure_futureদেখুন ডক্স । উদ্ধৃতিcreate_task() (added in Python 3.7) is the preferable way for spawning new tasks.
মাসি

24

সহজ উত্তর

  • কোনও কর্টিন ফাংশন ( async def) চালানো এটিকে চালায় না। এটি জেনারেটরের ফাংশন যেমন জেনারেটর অবজেক্ট প্রদান করে এমন একটি কর্টাইন অবজেক্ট প্রদান করে।
  • await কর্টাইন থেকে মানগুলি পুনরুদ্ধার করে, অর্থাৎ কর্টিনকে "কল" করে
  • eusure_future/create_task পরবর্তী পুনরাবৃত্তির উপর ইভেন্ট লুপে চালনার জন্য কর্টিনের সময়সূচী করুন (যদিও তাদের শেষ করার অপেক্ষায় নেই, ডেমনের থ্রেডের মতো)।

কিছু কোড উদাহরণ

প্রথমে কিছু শর্ত সাফ করুন:

  • কর্টিন ফাংশন, আপনি যার async def;
  • কর্টিন অবজেক্ট, আপনি যখন কোনও কর্টিন ফাংশন "কল" করেন তখন আপনি কী পেয়েছিলেন;
  • টাস্ক, ইভেন্ট লুপে চালানোর জন্য কোনও কর্টিন অবজেক্টের চারপাশে মোড়ানো একটি বস্তু।

কেস 1, awaitএকটি কর্টিনে

আমরা দুটি কর্টিন তৈরি করি, awaitএকটি, এবং create_taskঅন্যটি চালানোর জন্য ব্যবহার করি ।

import asyncio
import time

# coroutine function
async def p(word):
    print(f'{time.time()} - {word}')


async def main():
    loop = asyncio.get_event_loop()
    coro = p('await')  # coroutine
    task2 = loop.create_task(p('create_task'))  # <- runs in next iteration
    await coro  # <-- run directly
    await task2

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

আপনি ফলাফল পাবেন:

1539486251.7055213 - await
1539486251.7055705 - create_task

ব্যাখ্যা করা:

টাস্ক 1 সরাসরি সম্পাদন করা হয়েছিল এবং নিম্নলিখিত পুনরাবৃত্তিতে টাস্ক 2 কার্যকর করা হয়েছিল।

কেস 2, ইভেন্ট লুপের নিয়ন্ত্রণ দেয়

যদি আমরা মূল ফাংশনটি প্রতিস্থাপন করি তবে আমরা একটি আলাদা ফলাফল দেখতে পাচ্ছি:

async def main():
    loop = asyncio.get_event_loop()
    coro = p('await')
    task2 = loop.create_task(p('create_task'))  # scheduled to next iteration
    await asyncio.sleep(1)  # loop got control, and runs task2
    await coro  # run coro
    await task2

আপনি ফলাফল পাবেন:

-> % python coro.py
1539486378.5244057 - create_task
1539486379.5252144 - await  # note the delay

ব্যাখ্যা করা:

কল করার সময় asyncio.sleep(1), কন্ট্রোলটি ইভেন্ট লুপটিতে ফিরে আসে এবং লুপগুলি সঞ্চালনের জন্য কাজগুলি পরীক্ষা করে, তারপরে এটি তৈরি করা টাস্কটি চালায় create_task

মনে রাখবেন যে, আমরা প্রথমে কর্টিন ফাংশনটি আহ্বান করি, তবে তা awaitনয়, তাই আমরা কেবল একটি একক কর্টিন তৈরি করেছি, এবং এটি চালিত না করে। তারপরে, আমরা আবার কর্টিন ফাংশনটি কল করি এবং এটি একটি create_taskকলটিতে আবদ্ধ করি, ক্রিয়েট_টাস্ক প্রকৃতপক্ষে পরবর্তী পুনরাবৃত্তিতে চলার জন্য কর্টিনকে নির্ধারণ করে। সুতরাং, ফলস্বরূপ, create taskআগে কার্যকর করা হয় await

প্রকৃতপক্ষে, এখানে বক্তব্যটি হ'ল লুপটিকে নিয়ন্ত্রণ ফিরিয়ে দেওয়া, আপনি asyncio.sleep(0)একই ফলাফলটি দেখতে ব্যবহার করতে পারেন।

ফণা অধীনে

loop.create_taskআসলে কল asyncio.tasks.Task(), যা কল করবে loop.call_soon। এবং loop.call_soonটাস্কটি ভিতরে রাখবে loop._ready। লুপের প্রতিটি পুনরাবৃত্তির সময় এটি লুপের প্রতিটি কলব্যাকের জন্য পরীক্ষা করে cks_ প্রস্তুত এবং এটি চালায়।

asyncio.wait, asyncio.ensure_futureএবং asyncio.gatherপ্রকৃতপক্ষে loop.create_taskপ্রত্যক্ষ বা পরোক্ষভাবে কল করুন ।

ডক্সেও নোট করুন :

কলব্যাকগুলিকে যে ক্রমে নিবন্ধভুক্ত করা হয় সেটিতে ডাকা হয়। প্রতিটি কলব্যাক ঠিক একবার কল করা হবে।


4
একটি পরিষ্কার ব্যাখ্যা জন্য ধন্যবাদ! বলতে হবে, এটি একটি দুর্দান্ত ভয়ঙ্কর নকশা। উচ্চ-স্তরের এপিআই নিম্ন-স্তরের বিমূর্ততা ফাঁস করছে, যা এপিআইকে আরও জটিল করে তোলে।
বরিস বুর্কভ


সুন্দর ব্যাখ্যা! আমি মনে করি যে await task2কলটির প্রভাব স্পষ্ট করা যেতে পারে। উভয় উদাহরণে, loop.create_task () কলটি ইভেন্ট লুপের টাস্ক 2 কে নির্ধারিত করে। সুতরাং উভয় প্রাক্তনের মধ্যে আপনি মুছতে পারেন await task2এবং এখনও টাস্ক 2 শেষ পর্যন্ত চলবে। প্রাক্তন 2-তে আচরণটি অভিন্ন হবে, কারণ await task2আমি বিশ্বাস করি যে ইতিমধ্যে সম্পন্ন টাস্কের সময় নির্ধারণ করা হয়েছে (যা দ্বিতীয়বার চালিত হবে না), যদিও প্রাক্তন ১-এ আচরণটি কিছুটা আলাদা হবে কারণ টাস্ক 2 সম্পূর্ণ না হওয়া পর্যন্ত সম্পাদন করা হবে না। পার্থক্যটি দেখতে, print("end of main")প্রাক্তনটির মূলের শেষে যুক্ত করুন
এন্ড্রু

11

Https://github.com/python/asyncio/blob/master/asyncio/tasks.py#L346 এর সাথে যুক্ত ভিনসেন্টের একটি মন্তব্য , যা দেখায় যে আপনার জন্য wait()কর্টিনগুলিকে মোড়ানো ensure_future()!

অন্য কথায়, আমাদের ভবিষ্যতের প্রয়োজন, এবং কর্টাইনগুলি নীরবে তাদের মধ্যে রূপান্তরিত হবে।

আমি যখন উত্তর কোরিটাইন / ফিউচারের ব্যাচ করব তার একটি নির্দিষ্ট ব্যাখ্যা খুঁজে পাব আমি এই উত্তরটি আপডেট করব।


তার মানে যে একটি coroutine বস্তুর জন্য c, await cসমতূল্য await create_task(c)?
অ্যালেক্সি

3

বিডিএফএল [2013] থেকে

কাজ

  • এটি একটি ভবিষ্যতে আবৃত কর্টিন
  • ক্লাস টাস্ক ক্লাস ফিউচারের একটি সাবক্লাস
  • সুতরাং এটি খুব অপেক্ষা সঙ্গে কাজ করে!

  • এটি একটি খালি কর্টিনের থেকে কীভাবে আলাদা?
  • এটি অপেক্ষা না করে অগ্রগতি করতে পারে
    • যতক্ষণ আপনি অন্য কোনও কিছুর জন্য অপেক্ষা করেন, যেমন
      • অপেক্ষা করুন [কিছু_ও অন্য]

এটি মাথায় রেখে, ensure_futureএকটি টাস্ক তৈরির একটি নাম হিসাবে জ্ঞান তৈরি করে যেহেতু ভবিষ্যতের ফলাফলটি আপনি অপেক্ষা করছেন বা না করেন তা গণনা করা হবে (যতক্ষণ আপনি কোনও কিছুর অপেক্ষায় রয়েছেন)। আপনি ইভেন্টের অপেক্ষার সময় ইভেন্টের লুপটি আপনার টাস্কটি সম্পূর্ণ করতে দেয়। মনে রাখবেন যে, এ পাইথন 3.7 create_taskহয় পছন্দের পথ ভবিষ্যতে নিশ্চিত

দ্রষ্টব্য: আমি আধুনিকতার জন্য এখানে গুইডোর স্লাইডগুলিতে "ফলন" থেকে "অপেক্ষা" করতে পরিবর্তন করেছি।

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