Python协程遐想

自己在学习协程的时候,对一些东西拿捏的不准,甚至是不解,搞出这样的两个脚本来,让自己更糊涂了,请教了很多人,估计是因为这个问题很愚蠢,所以也没人搭理,后来自己想了想,发现结果很明了,结合之前学习到的理论,有了一些认识

image.png

1.同步脚本在执行的时候,print_time函数总共耗时是3s,因为该函数总共调用3次,每次sleep1s,该sleep操作是阻塞的。
2.协程方式的print_time耗时很少,函数中的sleep操作是非阻塞的。这里结果显示的调用6次,可以以上图那么解释,其实是上下文切换了6次。

# 脚本demo2_1.py
import asyncio
import datetime
import time

@asyncio.coroutine
def print_time():
    print(datetime.datetime.now())
    yield from asyncio.sleep(1)

@asyncio.coroutine
def display_date(loop):
    end_time = loop.time() + 5.0
    while True:
        yield from print_time()
        if (loop.time() + 1.0) >= end_time:
            break
        yield from asyncio.sleep(1)
    return "Coroutine is finished..."



if __name__ =='__main__':
    import cProfile
    start_time = time.time()
    if sys.platform == "win32":
        loops = asyncio.ProactorEventLoop()
        asyncio.set_event_loop(loops)
    else:
        loops = asyncio.get_event_loop()
    # Blocking call which returns when the hello_world() coroutine is done
    try:
        cProfile.run("loops.run_until_complete(display_date(loops))")
        # print(loops.run_until_complete(display_date(loops)))
        # print('Used: ', time.time()-start_time)
    except KeyboardInterrupt as e:
        for task in asyncio.Task.all_tasks():
            print(task.cancel())
        loops.stop()
        try:
            loops.run_forever()
        finally:
            loops.close()
# 脚本demo2_2.py
import datetime
import time



def print_time():
    print(datetime.datetime.now())
    time.sleep(1)

def display_date():
    end_time = time.time() + 5.0
    while True:
        print_time()
        if (time.time() + 1.0) >= end_time:
            break
        time.sleep(1)


if __name__ == '__main__':
    import cProfile
    start_time = time.time()
    cProfile.run("display_date()")
    print('Used: ', time.time() - start_time)

脚本demo2_1.py的运行结果如下:

2017-08-09 12:14:51.292522
2017-08-09 12:14:53.293397
2017-08-09 12:14:55.293789
         421 function calls in 5.002 seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.000    0.000    5.002    5.002 <string>:1(<module>)
        1    0.000    0.000    0.000    0.000 _weakrefset.py:38(_remove)
        1    0.000    0.000    0.000    0.000 _weakrefset.py:70(__contains__)
        2    0.000    0.000    0.000    0.000 _weakrefset.py:81(add)
        5    0.000    0.000    0.000    0.000 base_events.py:1325(_timer_handle_cancelled)
       12    0.000    0.000    5.002    0.417 base_events.py:1330(_run_once)
        2    0.000    0.000    0.000    0.000 base_events.py:1429(_set_coroutine_wrapper)
       24    0.000    0.000    0.000    0.000 base_events.py:1462(get_debug)
        1    0.000    0.000    0.000    0.000 base_events.py:176(_run_until_complete_cb)
        5    0.000    0.000    0.000    0.000 base_events.py:273(create_future)
        1    0.000    0.000    0.000    0.000 base_events.py:277(create_task)
       15    0.000    0.000    0.000    0.000 base_events.py:355(_check_closed)
        1    0.000    0.000    5.002    5.002 base_events.py:404(run_forever)
        1    0.000    0.000    5.002    5.002 base_events.py:432(run_until_complete)
        1    0.000    0.000    0.000    0.000 base_events.py:469(stop)
        1    0.000    0.000    0.000    0.000 base_events.py:514(is_running)
       26    0.000    0.000    0.000    0.000 base_events.py:518(time)
        5    0.000    0.000    0.000    0.000 base_events.py:527(call_later)
        5    0.000    0.000    0.000    0.000 base_events.py:548(call_at)
        7    0.000    0.000    0.000    0.000 base_events.py:564(call_soon)
        7    0.000    0.000    0.000    0.000 base_events.py:594(_call_soon)
        2    0.000    0.000    0.000    0.000 base_futures.py:23(isfuture)
        1    0.000    0.000    0.000    0.000 coroutines.py:268(iscoroutine)
        6    0.000    0.000    0.001    0.000 demo2_1.py:11(display_date)
        6    0.000    0.000    0.000    0.000 demo2_1.py:6(print_time)
        5    0.000    0.000    0.000    0.000 events.py:114(cancel)
       13    0.000    0.000    0.001    0.000 events.py:125(_run)
        5    0.000    0.000    0.000    0.000 events.py:147(__init__)
        5    0.000    0.000    0.000    0.000 events.py:192(cancel)
        6    0.000    0.000    0.000    0.000 events.py:621(_get_running_loop)
        2    0.000    0.000    0.000    0.000 events.py:632(_set_running_loop)
        5    0.000    0.000    0.000    0.000 events.py:666(get_event_loop)
       12    0.000    0.000    0.000    0.000 events.py:86(__init__)
        5    0.000    0.000    0.000    0.000 futures.py:344(_set_result_unless_cancelled)
        1    0.000    0.000    0.000    0.000 proactor_events.py:477(_loop_self_reading)
       12    0.000    0.000    0.000    0.000 proactor_events.py:539(_process_events)
       10    0.000    0.000    0.000    0.000 tasks.py:468(sleep)
        1    0.000    0.000    0.000    0.000 tasks.py:507(ensure_future)
       12    0.000    0.000    5.000    0.417 windows_events.py:417(select)
        1    0.000    0.000    0.000    0.000 windows_events.py:429(recv)
        1    0.000    0.000    0.000    0.000 windows_events.py:45(__init__)
        1    0.000    0.000    0.000    0.000 windows_events.py:606(_register_with_iocp)
        1    0.000    0.000    0.000    0.000 windows_events.py:616(_register)
       12    0.000    0.000    5.000    0.417 windows_events.py:660(_poll)
        5    0.000    0.000    0.000    0.000 {built-in method _heapq.heappop}
        5    0.000    0.000    0.000    0.000 {built-in method _heapq.heappush}
        1    0.000    0.000    0.000    0.000 {built-in method _overlapped.CreateIoCompletionPort}
       12    5.000    0.417    5.000    0.417 {built-in method _overlapped.GetQueuedCompletionStatus}
        1    0.000    0.000    0.000    0.000 {built-in method _thread.get_ident}
        1    0.000    0.000    5.002    5.002 {built-in method builtins.exec}
        2    0.000    0.000    0.000    0.000 {built-in method builtins.hasattr}
        3    0.000    0.000    0.000    0.000 {built-in method builtins.isinstance}
       24    0.000    0.000    0.000    0.000 {built-in method builtins.len}
        5    0.000    0.000    0.000    0.000 {built-in method builtins.max}
        3    0.000    0.000    0.000    0.000 {built-in method builtins.print}
       12    0.000    0.000    0.000    0.000 {built-in method math.ceil}
        3    0.000    0.000    0.000    0.000 {built-in method now}
        7    0.000    0.000    0.000    0.000 {built-in method nt.getpid}
        1    0.000    0.000    0.000    0.000 {built-in method sys.get_asyncgen_hooks}
        2    0.000    0.000    0.000    0.000 {built-in method sys.set_asyncgen_hooks}
       26    0.000    0.000    0.000    0.000 {built-in method time.monotonic}
        1    0.000    0.000    0.000    0.000 {method 'WSARecv' of '_overlapped.Overlapped' objects}
        2    0.000    0.000    0.000    0.000 {method 'add' of 'set' objects}
        1    0.000    0.000    0.000    0.000 {method 'add_done_callback' of '_asyncio.Future' objects}
        1    0.000    0.000    0.000    0.000 {method 'add_done_callback' of '_asyncio.Task' objects}
       12    0.000    0.000    0.000    0.000 {method 'append' of 'collections.deque' objects}
        5    0.000    0.000    0.000    0.000 {method 'cancelled' of '_asyncio.Future' objects}
       12    0.000    0.000    0.000    0.000 {method 'clear' of 'list' objects}
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
        1    0.000    0.000    0.000    0.000 {method 'discard' of 'set' objects}
        1    0.000    0.000    0.000    0.000 {method 'done' of '_asyncio.Task' objects}
        2    0.000    0.000    0.000    0.000 {method 'fileno' of '_socket.socket' objects}
       13    0.000    0.000    0.000    0.000 {method 'popleft' of 'collections.deque' objects}
        1    0.000    0.000    0.000    0.000 {method 'remove_done_callback' of '_asyncio.Task' objects}
        1    0.000    0.000    0.000    0.000 {method 'result' of '_asyncio.Task' objects}
        5    0.000    0.000    0.000    0.000 {method 'set_result' of '_asyncio.Future' objects}

脚本demo2_2.py运行结果如下:

2017-08-09 11:32:43.821214
2017-08-09 11:32:45.822848
2017-08-09 11:32:47.823607
         22 function calls in 5.002 seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.000    0.000    5.002    5.002 <string>:1(<module>)
        1    0.000    0.000    5.002    5.002 demo2_2.py:10(display_date)
        3    0.000    0.000    3.002    1.001 demo2_2.py:6(print_time)
        1    0.000    0.000    5.002    5.002 {built-in method builtins.exec}
        3    0.000    0.000    0.000    0.000 {built-in method builtins.print}
        3    0.000    0.000    0.000    0.000 {built-in method now}
        5    5.002    1.000    5.002    1.000 {built-in method time.sleep}
        4    0.000    0.000    0.000    0.000 {built-in method time.time}
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}


Used:  5.006526947021484

总结: 在实际应用中,当我们需要使用协程方式来提过效率时,注意,需要使用异步非阻塞的形式来实现,否则就像time.sleep(),即使我们将它放在了协程实现中,也是达不到效率提高的,退化成了同步实现
举例验证:

import time
import datetime
import tornado.ioloop
import tornado.web
import tornado.gen

class MainHandler(tornado.web.RequestHandler):

    @tornado.gen.coroutine
    def get(self):
        yield tornado.gen.Task(self.blocksleep)
        self.write("Hello, world")

    @tornado.gen.coroutine
    def blocksleep(self):
        time.sleep(20)
        return


def make_app():
    return tornado.web.Application([
        (r"/", MainHandler),
    ])

if __name__ == "__main__":
    app = make_app()
    app.listen(8888)
    tornado.ioloop.IOLoop.current().start()

那么,当我们确实需要异步调用阻塞形式的代码时,有什么办法呢,可以使用线程, 还是上面的例子

import time
import datetime
import tornado.ioloop
import tornado.web
import tornado.gen
import tornado.concurrent
from concurrent.futures import ThreadPoolExecutor

class MainHandler(tornado.web.RequestHandler):
    executor = ThreadPoolExecutor(10)
    @tornado.gen.coroutine
    def get(self):
        future = ThreadPoolExecutor().submit(self.blocksleep)
        yield tornado.gen.with_timeout(datetime.timedelta(10), future,
                                       quiet_exceptions=tornado.gen.TimeoutError)
        self.write("Hello, world")

    @tornado.concurrent.run_on_executor
    def blocksleep(self):
        time.sleep(20)
        return


def make_app():
    return tornado.web.Application([
        (r"/", MainHandler),
    ])

if __name__ == "__main__":
    app = make_app()
    app.listen(8888)
    tornado.ioloop.IOLoop.current().start()
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 220,639评论 6 513
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,093评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 167,079评论 0 357
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,329评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,343评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,047评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,645评论 3 421
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,565评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,095评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,201评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,338评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,014评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,701评论 3 332
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,194评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,320评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,685评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,345评论 2 358

推荐阅读更多精彩内容