পাইথনে অ্যাসিঙ্ক্রোনাস পদ্ধতি কল?


178

আমি ভাবছিলাম পাইথনে অ্যাসিক্রোনাস পদ্ধতিতে কল করার জন্য কোনও লাইব্রেরি আছে কিনা । আপনি ভালো কিছু করতে পারলে দুর্দান্ত হবে

@async
def longComputation():
    <code>


token = longComputation()
token.registerCallback(callback_function)
# alternative, polling
while not token.finished():
    doSomethingElse()
    if token.finished():
        result = token.result()

বা অ্যাসিঙ্ক নন রুটিনকে অ্যাসিঙ্ক্রোনালি কল করতে

def longComputation()
    <code>

token = asynccall(longComputation())

ভাষার মূলতে স্থানীয় হিসাবে আরও পরিশ্রুত কৌশল অর্জন করা দুর্দান্ত হবে। এটা বিবেচনা করা হয়েছিল?


পাইথন ৩.৪ অনুসারে: docs.python.org/3/library/asyncio.html (৩.৩ এর একটি ব্যাকপোর্ট asyncএবং await3.5 থেকে চকচকে নতুন এবং বাক্য গঠন)।
jonrsharpe

কোনও কলব্যাক মেকানিজম নেই, তবে আপনি অভিধানে একত্রিত করতে পারেন এবং এটি পাইথনের মাল্টিপ্রসেসিং মডিউলের উপর ভিত্তি করে। আমি নিশ্চিত আপনি কলব্যাক হিসাবে সজ্জিত ফাংশনে আরও একটি পরামিতি যুক্ত করতে পারেন। github.com/alex-sherman/deco
রাজারভির্মা

শুরু করতে. অফিসিয়াল ডকুমেন্টেশন - docs.python.org/3/library/concurrency.html
আদর্শ মাদ্রেচা

উত্তর:


141

আপনি পাইথন ২.6 এ যুক্ত মাল্টিপ্রসেসিং মডিউলটি ব্যবহার করতে পারেন । আপনি প্রক্রিয়াগুলির পুলগুলি ব্যবহার করতে পারেন এবং তারপরে সংযোজনীয়ভাবে ফলাফলগুলি পেতে পারেন:

apply_async(func[, args[, kwds[, callback]]])

উদাহরণ:

from multiprocessing import Pool

def f(x):
    return x*x

if __name__ == '__main__':
    pool = Pool(processes=1)              # Start a worker processes.
    result = pool.apply_async(f, [10], callback) # Evaluate "f(10)" asynchronously calling callback when finished.

এটি কেবল একটি বিকল্প। আপনি চান তা অর্জন করতে এই মডিউলটি প্রচুর সুবিধাদি সরবরাহ করে। এছাড়াও এটি থেকে একটি সজ্জা তৈরি করা সত্যিই সহজ হবে।


5
দুর্ভাগ্যক্রমে লুকাস এস, আপনার উদাহরণটি কার্যকর হয় না। কলব্যাক ফাংশন কখনই কল হয় না।
ডেটাগ্রিড

6
এটি সম্ভবত মনে রাখা উচিত যে এটি একটি প্রক্রিয়ার মধ্যে পৃথক থ্রেডের চেয়ে পৃথক প্রক্রিয়া তৈরি করে। এটি কিছু জড়িত হতে পারে।
ব্যবহারকারী 47741

11
এটি কাজ করে: ফলাফল = পুল। অ্যাপ্লিকেশন_সায়েন্স (এফ, [10], কলব্যাক = সমাপ্তি)
এমজে

6
অজগরকে সত্যই অ্যাসিক্রোনাকলভাবে কিছু করার জন্য নতুন প্রক্রিয়াগুলিকে স্প্যান করার জন্য মাল্টিপ্রসেসিং মডিউলটি ব্যবহার করা দরকার। কেবল নতুন থ্রেড তৈরি করা এখনও গ্লোবাল ইন্টারপ্রেটার লকের করুণায় রয়েছে যা পাইথন প্রক্রিয়াটিকে একসাথে একাধিক কাজ করতে বাধা দেয়।
দ্রহকর

2
আপনি যদি এই সমাধানটি ব্যবহার করার সময় কোনও নতুন প্রক্রিয়া ঘটাতে না চান - আমদানিতে এতে পরিবর্তন করুন from multiprocessing.dummy import Pool। মাল্টিপ্রসেসিং.ডামির প্রক্রিয়াগুলির পরিবর্তে থ্রেডগুলিতে ঠিক একই আচরণ প্রয়োগ করা হয়েছে
আলমগ কোহেন

203

কিছুটা এইরকম:

import threading

thr = threading.Thread(target=foo, args=(), kwargs={})
thr.start() # Will run "foo"
....
thr.is_alive() # Will return whether foo is running currently
....
thr.join() # Will wait till "foo" is done

আরও তথ্যের জন্য ডকুমেন্টেশনটি https://docs.python.org/library/threading.html এ দেখুন ।


1
হ্যাঁ, আপনি যদি কেবল অ্যাসিক্রোনালিকভাবে কাজগুলি করতে চান তবে কেন কেবল থ্রেড ব্যবহার করবেন না? সমস্ত থ্রেড প্রক্রিয়া অপেক্ষা হালকা ওজন
kk1957

22
গুরুত্বপূর্ণ দ্রষ্টব্য: "গ্লোবাল ইন্টারপ্রিটার লক" এর কারণে থ্রেডগুলির মানক প্রয়োগ (সিপিথন) গণনা-সীমাবদ্ধ কাজগুলিতে সহায়তা করবে না। লাইব্রেরি ডকটি দেখুন: লিংক
দ্রবণীয় ফিশ

3
থ্রেড.জাইন () ব্যবহার করা কি আসলেই অ্যাসিক্রোনাস? আপনি যদি কোনও থ্রেড (যেমন একটি ইউআই থ্রেড) অবরুদ্ধ করতে না চান এবং এতে কিছুক্ষণ লুপ ব্যবহার করে এক টন সংস্থান ব্যবহার করতে চান না?
এমগামেরজ

1
@ এমগামের্জ যোগ যোগসূত্র। আপনি থ্রেডটিকে মৃত্যুদন্ড কার্যকর করার ফলাফলটিকে কিছু কাতারে রেখে দিতে পারেন, বা / এবং কলব্যাক কল করতে পারেন। না হলে আপনি জানেন না কখন এটি হয়ে গেছে (যদি তা হয়)।
দ্রকোশা

1
থ্রেড এক্সিকিউশনের শেষে কলব্যাক ফাংশনটি কল করা যেমন আপনি মাল্টিপ্রসেসিং দিয়ে করতে পারেন তেমন কি সম্ভব
ool পুল

49

পাইথন 3.5 হিসাবে, আপনি অ্যাসিঙ্ক ফাংশনগুলির জন্য বর্ধিত জেনারেটর ব্যবহার করতে পারেন।

import asyncio
import datetime

বর্ধিত জেনারেটর সিনট্যাক্স:

@asyncio.coroutine
def display_date(loop):
    end_time = loop.time() + 5.0
    while True:
        print(datetime.datetime.now())
        if (loop.time() + 1.0) >= end_time:
            break
        yield from asyncio.sleep(1)


loop = asyncio.get_event_loop()
# Blocking call which returns when the display_date() coroutine is done
loop.run_until_complete(display_date(loop))
loop.close()

নতুন async/awaitবাক্য গঠন:

async def display_date(loop):
    end_time = loop.time() + 5.0
    while True:
        print(datetime.datetime.now())
        if (loop.time() + 1.0) >= end_time:
            break
        await asyncio.sleep(1)


loop = asyncio.get_event_loop()
# Blocking call which returns when the display_date() coroutine is done
loop.run_until_complete(display_date(loop))
loop.close()

8
@ কর্ণাবিহ, ওপির "ডিএফ লংকমপুটেশন ()" ফাংশন অন্তর্ভুক্ত করার জন্য আপনি কি সেই উদাহরণটি বাড়িয়ে দিতে পারেন? বেশিরভাগ উদাহরণে "অপেক্ষা করুন অ্যাসিনসিও.স্লাইপ (1)" ব্যবহার করুন তবে লংকমপুটেশন () যদি ফিরে আসে তবে একটি ডাবল বলুন, আপনি কেবল "প্রতীক্ষিত লংকমপুটেশন ()" ব্যবহার করতে পারবেন না।
ফ্যাব

ভবিষ্যতে দশ বছর এবং এটি এখনই গ্রহণযোগ্য উত্তর হওয়া উচিত। আপনি যখন পাইথন ৩.৫ এ অ্যাসিঙ্ক সম্পর্কে কথা বলেন + তখন যা মনে আসে তা অ্যাসিনসিও এবং অ্যাসিঙ্ক কীওয়ার্ড হওয়া উচিত।
zeh

31

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


1
বিশেষত, twisted.internet.defer ( twistedmatrix.com/documents/8.2.0/api/… ) দেখুন।
নিকোলাস রিলে

21

আপনি নিজের ফাংশনগুলিকে অবিচ্ছিন্ন করতে কোনও ডেকরেটার বাস্তবায়ন করতে পারেন, যদিও এটি কিছুটা জটিল। দ্যmultiprocessingমডিউল সামান্য quirks এবং আপাতদৃষ্টিতে নির্বিচারে সীমাবদ্ধতা পূর্ণ - সব আরো, যদিও একটি বন্ধুত্বপূর্ণ ইন্টারফেস পিছনে এটা encapsulate করতে কারণ।

from inspect import getmodule
from multiprocessing import Pool


def async(decorated):
    r'''Wraps a top-level function around an asynchronous dispatcher.

        when the decorated function is called, a task is submitted to a
        process pool, and a future object is returned, providing access to an
        eventual return value.

        The future object has a blocking get() method to access the task
        result: it will return immediately if the job is already done, or block
        until it completes.

        This decorator won't work on methods, due to limitations in Python's
        pickling machinery (in principle methods could be made pickleable, but
        good luck on that).
    '''
    # Keeps the original function visible from the module global namespace,
    # under a name consistent to its __name__ attribute. This is necessary for
    # the multiprocessing pickling machinery to work properly.
    module = getmodule(decorated)
    decorated.__name__ += '_original'
    setattr(module, decorated.__name__, decorated)

    def send(*args, **opts):
        return async.pool.apply_async(decorated, args, opts)

    return send

নীচের কোডটি সাজসজ্জার ব্যবহারের চিত্রিত করে:

@async
def printsum(uid, values):
    summed = 0
    for value in values:
        summed += value

    print("Worker %i: sum value is %i" % (uid, summed))

    return (uid, summed)


if __name__ == '__main__':
    from random import sample

    # The process pool must be created inside __main__.
    async.pool = Pool(4)

    p = range(0, 1000)
    results = []
    for i in range(4):
        result = printsum(i, sample(p, 100))
        results.append(result)

    for result in results:
        print("Worker %i: sum value is %i" % result.get())

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


এটি সেরা উত্তর হওয়া উচিত। আমি পছন্দ করি কিভাবে এটি মূল্য ফিরিয়ে দিতে পারে। থ্রেডের মতো নয় যা নিছক অবিচ্ছিন্নভাবে চলছে।
আমিনাহ নুরাইনি

16

মাত্র

import threading, time

def f():
    print "f started"
    time.sleep(3)
    print "f finished"

threading.Thread(target=f).start()

8

আপনি ইভেন্টলেটলেট ব্যবহার করতে পারেন। এটি আপনাকে সিঙ্ক্রোনাস কোড বলে মনে হচ্ছে তা লিখতে দেয়, তবে এটি নেটওয়ার্কের মধ্যে অবিচ্ছিন্নভাবে পরিচালনা করে।

এখানে একটি সুপার ন্যূনতম ক্রলার উদাহরণ:

urls = ["http://www.google.com/intl/en_ALL/images/logo.gif",
     "https://wiki.secondlife.com/w/images/secondlife.jpg",
     "http://us.i1.yimg.com/us.yimg.com/i/ww/beta/y3.gif"]

import eventlet
from eventlet.green import urllib2

def fetch(url):

  return urllib2.urlopen(url).read()

pool = eventlet.GreenPool()

for body in pool.imap(fetch, urls):
  print "got body", len(body)

7

আমার সমাধানটি হ'ল:

import threading

class TimeoutError(RuntimeError):
    pass

class AsyncCall(object):
    def __init__(self, fnc, callback = None):
        self.Callable = fnc
        self.Callback = callback

    def __call__(self, *args, **kwargs):
        self.Thread = threading.Thread(target = self.run, name = self.Callable.__name__, args = args, kwargs = kwargs)
        self.Thread.start()
        return self

    def wait(self, timeout = None):
        self.Thread.join(timeout)
        if self.Thread.isAlive():
            raise TimeoutError()
        else:
            return self.Result

    def run(self, *args, **kwargs):
        self.Result = self.Callable(*args, **kwargs)
        if self.Callback:
            self.Callback(self.Result)

class AsyncMethod(object):
    def __init__(self, fnc, callback=None):
        self.Callable = fnc
        self.Callback = callback

    def __call__(self, *args, **kwargs):
        return AsyncCall(self.Callable, self.Callback)(*args, **kwargs)

def Async(fnc = None, callback = None):
    if fnc == None:
        def AddAsyncCallback(fnc):
            return AsyncMethod(fnc, callback)
        return AddAsyncCallback
    else:
        return AsyncMethod(fnc, callback)

এবং অনুরোধ হিসাবে ঠিক কাজ করে:

@Async
def fnc():
    pass

5

এর মতো কিছু আমার পক্ষে কাজ করে, তারপরে আপনি ফাংশনটি কল করতে পারবেন এবং এটি নিজেই একটি নতুন থ্রেডে প্রেরণ করবে।

from thread import start_new_thread

def dowork(asynchronous=True):
    if asynchronous:
        args = (False)
        start_new_thread(dowork,args) #Call itself on a new thread.
    else:
        while True:
            #do something...
            time.sleep(60) #sleep for a minute
    return

2

থ্রেড ব্যবহার না করার কোনও কারণ আছে কি? আপনি threadingক্লাস ব্যবহার করতে পারেন । পরিবর্তে finished()ফাংশন ব্যবহার করুন isAlive()result()ফাংশন পারা join()থ্রেড এবং ফলাফল পুনরুদ্ধার। এবং, যদি আপনি পারেন তবে কনস্ট্রাক্টরের নির্দিষ্ট করা ফাংশনটি কল করতে ফাংশন run()__init__ক্রিয়াকলাপগুলি ওভাররাইড করুন এবং শ্রেণীর উদাহরণে কোথাও মানটি সংরক্ষণ করুন।


2
এটি যদি একটি গণনামূলকভাবে ব্যয়বহুল ফাংশন থ্রেডিংয়ের কিছু না পায় (এটি সম্ভবত জিনিসগুলি ধীরে ধীরে ধীরে ধীরে করে দেবে) যেহেতু জিলের কারণে পাইথন প্রক্রিয়াটি একটি সিপিইউ কোরের মধ্যে সীমাবদ্ধ।
কর্ট 21

2
@ কুর্ট, যদিও এটি সত্য, ওপিতে উল্লেখ করা হয়নি যে পারফরম্যান্সই তাঁর উদ্বেগ। অ্যাসিক্রোনাস আচরণের জন্য অন্যান্য কারণগুলিও রয়েছে ...
পিটার হ্যানসেন

অ্যাসিক্রোনাস মেথড কলটি হত্যার বিকল্প পেতে চাইলে পাইথনের থ্রেডগুলি দুর্দান্ত নয়, যেহেতু পাইথনের কেবল মূল থ্রেডই সংকেত পেয়ে থাকে।
সিভিফ্যান

2

আপনি কনক্রন্ট.ফিউচারগুলি ব্যবহার করতে পারেন (পাইথন ৩.২-এ যোগ করেছেন)।

import time
from concurrent.futures import ThreadPoolExecutor


def long_computation(duration):
    for x in range(0, duration):
        print(x)
        time.sleep(1)
    return duration * 2


print('Use polling')
with ThreadPoolExecutor(max_workers=1) as executor:
    future = executor.submit(long_computation, 5)
    while not future.done():
        print('waiting...')
        time.sleep(0.5)

    print(future.result())

print('Use callback')
executor = ThreadPoolExecutor(max_workers=1)
future = executor.submit(long_computation, 5)
future.add_done_callback(lambda f: print(f.result()))

print('waiting for callback')

executor.shutdown(False)  # non-blocking

print('shutdown invoked')

এটি একটি দুর্দান্ত উত্তর, কারণ এখানে কেবলমাত্র এটিই কলব্যাকগুলির সাথে থ্রেডপুলের সম্ভাবনা দেয়
রেদা

দুর্ভাগ্যক্রমে, এটি "গ্লোবাল ইন্টারপ্রেটার লক" থেকেও ভুগছে। লাইব্রেরি ডক দেখুন: লিঙ্ক । পাইথন 3.7
অ্যালেক্স

0

আপনি প্রক্রিয়া ব্যবহার করতে পারেন। যদি আপনি এটি চিরতরে চালাতে চান তবে আপনার মধ্যে (নেটওয়ার্কিংয়ের মতো) কাজ করার সময়:

from multiprocessing import Process
def foo():
    while 1:
        # Do something

p = Process(target = foo)
p.start()

আপনি যদি একবার এটি চালাতে চান তবে এটি করুন:

from multiprocessing import Process
def foo():
    # Do something

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