asyncio.ensure_future, BaseEventLoop.create_task ve basit coroutine?


101

Asyncio'da aynı işlemi çeşitli tatlarda yapan birkaç temel Python 3.5 öğreticisi gördüm. Bu kodda:

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)

futuresDeğişkeni tanımlayan yukarıdaki üç varyantın tümü aynı sonucu elde eder; görebildiğim tek fark, üçüncü varyantla yürütmenin düzensiz olmasıdır (çoğu durumda önemli olmamalıdır). Başka bir fark var mı? En basit varyantı kullanamayacağım durumlar var mı (düz eşgüdüm listesi)?

Yanıtlar:


122

Gerçek bilgi:

Python 3.7'den başlayarak asyncio.create_task(coro)üst düzey fonksiyon bu amaçla eklendi .

Zaman zamanlarından görev oluşturmanın başka yolları yerine onu kullanmalısınız. Bununla birlikte, keyfi olarak beklenebilir bir görev oluşturmanız gerekiyorsa, kullanmalısınız asyncio.ensure_future(obj).


Eski bilgi:

ensure_future vs create_task

ensure_futureoluşturmak için bir yöntemdir Taskgelen coroutine. create_taskArgümana dayalı olarak farklı şekillerde görevler oluşturur ( eşgüdümler ve geleceğe benzer nesneler için kullanımı dahil ).

create_tasksoyut bir yöntemdir AbstractEventLoop. Farklı olay döngüleri bu işlevi farklı şekillerde uygulayabilir.

Görev ensure_futureoluşturmak için kullanmalısınız . create_taskYalnızca kendi olay döngü türünüzü uygulayacaksanız ihtiyacınız olacak.

Güncelleme:

@ bj0, Guido'nun bu konudaki cevabına işaret etti :

Buradaki nokta, ensure_future()bir coroutine veya a olabilen bir şeye sahipseniz Future(ikincisi, a'yı içerir, Taskçünkü bu bir alt sınıfıdır Future) ve üzerinde yalnızca tanımlanmış bir yöntemi çağırmak istiyorsanız Future(muhtemelen tek faydalı örnek olmak cancel()). Zaten bir Future(veya Task) olduğunda bu hiçbir şey yapmaz; Bir eşyordam olduğunda sarar bir onu Task.

Bir coroutine sahip olduğunuzu biliyorsanız ve bunun planlanmasını istiyorsanız, kullanılacak doğru API create_task(). ensure_future()Aramanız gereken tek zaman, bir coroutine veya a'yı kabul eden bir API (asenkronizasyonun kendi API'lerinin çoğu gibi) sağladığınız Futureve ona sahip olmanızı gerektiren bir şey yapmanız gerektiği zamandır Future.

ve sonra:

Sonunda hala bunun ensure_future()nadiren ihtiyaç duyulan bir işlevsellik parçası için uygun şekilde belirsiz bir isim olduğuna inanıyorum . Bir eşdizimden görev oluştururken, uygun şekilde adlandırılmış olanı kullanmalısınız loop.create_task(). Belki bunun için bir takma ad olmalı asyncio.create_task()?

Benim için şaşırtıcı. ensure_futureBaştan sona kullanmak için ana motivasyonum, loop üyesine kıyasla daha üst düzey bir işlev olmasıydı create_task(tartışma , ekleme asyncio.spawnveya gibi bazı fikirler içerirasyncio.create_task ).

Ayrıca benim görüşüme göre, Awaitableyalnızca koroutinler yerine herhangi birini işleyebilen evrensel işlevi kullanmanın oldukça uygun olduğunu söyleyebilirim .

Bununla birlikte, Guido'nun cevabı açıktır: "Bir eşdizimden bir görev oluştururken, uygun şekilde adlandırılmış olanı kullanmalısınız loop.create_task()"

Eşgörünümler ne zaman görevlere sarılmalıdır?

Coroutine'i Göreve Sarma - bu coroutini "arka planda" başlatmanın bir yoludur. İşte örnek:

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

Çıktı:

first
long_operation started
second
long_operation finished

asyncio.ensure_future(long_operation())Sadece await long_operation()farkı hissetmek için yerine koyabilirsiniz .


3
Guido'ya görecreate_task , normalde ihtiyaç duymamanız
bj0

@ bj0 bu bağlantı için teşekkür ederim. Cevabı bu tartışmadan bilgi ekleyerek güncelledim.
Mikhail Gerasimov

yok ensure_futureotomatik olarak oluşturulur ekler Taskana olay döngü?
AlQuemist

@AlQuemist oluşturduğunuz her coroutine, gelecek veya görev otomatik olarak daha sonra yürütüleceği bir olay döngüsüne bağlanır. Varsayılan olarak, geçerli iş parçacığı için geçerli olay döngüsüdür, ancak loopanahtar sözcük bağımsız değişkenini kullanarak başka bir olay döngüsü belirtebilirsiniz ( bkz. Sure_future imzası ).
Mikhail Gerasimov

2
@laycat ikinci çağrıda kontrolü olay döngüsüne döndürmek için awaitiçinde ihtiyacımız var msg(). Olay döngüsü bir kez alındığında kontrol başlayabilecektir long_operation(). ensure_futureCoroutine'in mevcut yürütme akışıyla eşzamanlı olarak nasıl çalıştırılacağını göstermek için yapılmıştır .
Mikhail Gerasimov

47

create_task()

  • eş anlamlıları kabul eder,
  • Görev döndürür,
  • döngü bağlamında çağrılır.

ensure_future()

  • Futures, coroutine, waitable objects,
  • Görev (veya Gelecek geçtiyse Gelecek) döndürür.
  • verilen argüman kullandığı bir korutin ise create_task,
  • döngü nesnesi geçilebilir.

Gördüğünüz gibi, create_task daha spesifiktir.


async create_task veya sure_future olmadan işlev

Basit çağırma asyncişlevi coroutine döndürür

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

Ve gatherbaşlık altında ensure_futureargümanların gelecek olduğunu garanti ettiğinden ( ), açıkça ensure_futuregereksizdir.

Benzer soru loop.create_task, asyncio.async / sure_future ve Task arasındaki fark nedir?


14

Not: Yalnızca Python 3.7 için geçerlidir (Python 3.5 için önceki cevaba bakın ).

Resmi belgelerden:

asyncio.create_task(Python 3.7'de eklenmiştir), yerine yeni görevler üretmenin tercih edilen yoludur ensure_future().


Detay:

Şimdi, Python 3.7'den itibaren, 2 üst düzey sarmalayıcı işlevi vardır (benzer ancak farklı):

Kesinlikle, bu sarmalayıcı işlevlerinin ikisi de aramanıza yardımcı olacaktır BaseEventLoop.create_task. Tek fark, ensure_futureherhangi bir awaitablenesneyi kabul etmek ve onu bir Geleceğe dönüştürmenize yardımcı olmaktır. Ayrıca event_loopiçinde kendi parametrenizi de sağlayabilirsiniz ensure_future. Ve bu özelliklere ihtiyacınız olup olmadığına bağlı olarak, hangi sargıyı kullanacağınızı seçebilirsiniz.


Sanırım belgelenmemiş başka bir fark var: döngüyü çalıştırmadan önce asyncio.create_task'ı çağırmaya çalışırsanız, asyncio.create_task bir çalışan döngü beklediği için bir sorun yaşarsınız. Ancak bu durumda asyncio.ensure_future kullanabilirsiniz, çünkü çalışan bir döngü bir gereklilik değildir.
coelhudo

4

örneğiniz için, üç tür de eşzamansız olarak çalışır. tek fark, üçüncü örnekte, 10 eşutinin tamamını önceden oluşturmuş ve döngüye birlikte göndermiş olmanızdır. yani yalnızca sonuncusu rastgele çıktı verir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.