2015-07-22 61 views
2

已經運行添加到循環希望下面的代碼解釋了什麼是我想要做的比這個問題更稱號。如何協同程序/任務從「阻擋功能」在循環

import asyncio 
import time 

loop = asyncio.get_event_loop() 

class Foo(object): 
    def __init__(self, num): 
     self.num = num 
    @property 
    def number(self): 
     # Somehow need to run get_number() in the loop. 
     number = self.get_number() 
     return number 
    @asyncio.coroutine 
    def get_number(self): 
     yield from loop.run_in_executor(None, time.sleep, self.num) 
     return self.num 


@asyncio.coroutine 
def runner(num): 
    print("getting Foo({})".format(num)) 
    foo = Foo(num) 
    yield from asyncio.sleep(1) 
    print("accessing number property of Foo({})".format(num)) 
    number = foo.number 
    print("Foo({}) number is {}".format(num, number)) 


tasks = [ 
    asyncio.async(runner(3)), 
    asyncio.async(runner(1)), 
    ] 
go = loop.run_until_complete(asyncio.wait(tasks)) 

我不能在number函數中做什麼,其中註釋是。我嘗試了各種各樣的東西,但實際上我只是「一直在牆上扔****,希望有東西粘住」。

這是this question的後續行動。我想訪問該屬性而不做yield from,因爲我需要從模板(例如mako)訪問該屬性,並且隨處寫入yield from並不理想(可能考慮到mako可能無法阻止)。在一個完美的世界裏,我會用reify decorator來運行所有這些。

如果我想用yield from,代碼將是相當簡單的。

class Foo(object): 
    def __init__(self, num): 
     self.num = num 
    @property 
    @asyncio.coroutine 
    def number(self): 
     yield from loop.run_in_executor(None, time.sleep, self.num) 
     return self.num 


@asyncio.coroutine 
def runner(num): 
    print("getting Foo({})".format(num)) 
    foo = Foo(num) 
    yield from asyncio.sleep(1) 
    print("accessing number property of Foo({})".format(num)) 
    number = yield from foo.number 
    print("Foo({}) number is {}".format(num, number)) 

#getting Foo(3) 
#getting Foo(1) 
#accessing number property of Foo(3) 
#accessing number property of Foo(1) 
#Foo(1) number is 1 
#Foo(3) number is 3 

我發現this answer的話題,但我看不出做過添加回調將如何與我的工作流程工作。

+2

對不起,你所要求的不可能的事情。 如果沒有'yield from',沒有辦法從協同程序中獲得價值。 –

+0

我在哪裏註釋'#需要在循環中運行get_number().'是我希望創建未來任務並將其放到循環中,從而暫停當前函數 - 我只是不知道如何去做吧。例如,將'number = self.get_number()'改成'number = loop.create_task(self.get_number())'。這有可能嗎? – neRok

+1

@neRok暫停當前運行的函數的唯一方法是使用'yield from',這意味着它必須是一個協程。您可以使用'loop.create_task(self.get_number())'將事件循環添加到事件循環中,就像您所建議的那樣,但是直到實際調用'create_task'的方法將控制權還給事件循環,或者通過返回或者進行一次使用'yield from'的調用。將'asyncio'的代碼集成到同步代碼中並不會像你希望的那樣工作。 – dano

回答

1

你所要求的,是不可能的,因爲當你在你的主線程(要調用foo.number而不屈服,你需要明確的回饋控制,主循環。這正是yield from一樣。

不這樣做,你需要運行調用foo.number在一個單獨的線程,將能夠阻止(無論從收益率)的功能和等待get_number的結果,而不會阻塞主循環

0

從您的屏蔽功能而非計算值中,您應返回asyncio.Future

return loop.create_task(self.get_number()) 

當你得到這個在您的異步runner可以等待這樣的結果:

number = await foo.number 

全部測試用例:

def test_future(): 
    loop = asyncio.get_event_loop() 

    async def target(x: int) -> int: 
     loop.run_in_executor(None, time.sleep, 0.1) 
     return x + 1 

    def intermediate(x: int) -> asyncio.Future: 
     return loop.create_task(target(x)) 

    async def main(): 
     future = intermediate(5) 
     logger.debug('intermediate future = %r', future) 
     value = await future 
     assert value == 6 

    try: 
     loop.create_task(main()) 
     loop.call_later(0.5, loop.stop) 
     loop.run_forever() 
    finally: 
     loop.close()