同步
概念
同步就是按部就班的依次執行我們的代碼
進階
但是有些情況我們有一些比較耗時的從操作,比如去別的地方拿點資源,去其他網站請求數據,去訪問數據庫,上傳文件等等,所以這里面優點瑕疵,有小編一一道來
比如這樣
''' 本模塊的功能:<同步異步demo>'''# 這個就相等于一個客戶端的請求
import time# 添加一個耗時的操作
def longIO():print("開始耗時操作")time.sleep(5)print("結束耗時操作")def reqA():print("開始處理reqA")longIO()print("結束處理reqA")# 這個就相等于另一個客戶端的請求
def reqB():print("開始處理reqB")print("結束處理reqB")
def main():# 這就是同步在處理reqA()reqB()while True:'''# 如果你要想寫死循環,你不要直接寫死循環,你得睡一睡# 為什么要睡一睡呢,因為你要是不睡你會發現你的CPU利用率占100%'''time.sleep(0.1)
if __name__ == '__main__':main()
結果
開始處理reqA
開始耗時操作# 這里等待了5秒鐘
結束耗時操作
結束處理reqA
開始處理reqB
結束處理reqB
在請求中添加了一個耗時的操作,導致了我們的同步的效率特別低了,這樣也體現不出我們tornado高效的優點
'''┌─┐ ┌─┐ + +┌──┘ ┴───────┘ ┴──┐++│ ││ ─── │++ + + +███████───███████ │+│ │+│ ─┴─ ││ │└───┐ ┌───┘│ ││ │ + +│ ││ └──────────────┐│ ││ ├─┐│ ┌─┘│ │└─┐ ┐ ┌───────┬──┐ ┌──┘ + + + +│ ─┤ ─┤ │ ─┤ ─┤└──┴──┘ └──┴──┘ + + + +神獸保佑代碼無BUG!'''
異步
你干一件事情的同事又去干另一件事情
概述
對于耗時的操作,會交給別人(另一個線程)處理,我們繼續向下執行,當別人結束耗時操作后,再將處理結果返回給我們
回調函數實現異步
異步其實我們已經用了,js里面有一個很明顯的異步,就是在我們發ajax的時候,當我們發完ajax就去干別的活去了,后來ajax有響應了我們才搭理他
來來來,代碼演示如下:
'''
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Created by victor
'''
# 本模塊的功能:<異步演示demo># 這個就相等于一個客戶端的請求
import time
import threading
''' 添加一個耗時的操作''''''
這樣就會有一個問題,這個run()函數的返回值我們接受不到
為了解決這個問題,我們需要寫一個函數,這個函數叫做回調函數
'''
def longIO(callback):def run(cb):print("開始耗時操作")time.sleep(3)print("結束耗時操作")cb("victor is a wonderful man")threading.Thread(target=run,args=(callback,)).start()
這個longio這部分 ,就像ajax一樣都不用我們來寫了
def finish(data):print("開始處理回調函數")print("接受到longIO的數據為:",data)print("結束處理回調函數")def reqA():print("開始處理reqA")longIO(finish)print("結束處理reqA")# 這個就相等于另一個客戶端的請求
def reqB():print("開始處理reqB")time.sleep(1)print("結束處理reqB")
def main():# 這就是同步在處理reqA()reqB()while True:'''# 如果你要想寫死循環,你不要直接寫死循環,你得睡一睡# 為什么要睡一睡呢,因為你要是不睡你會發現你的CPU利用率占100%'''time.sleep(0.1)if __name__ == '__main__':main()
異步只是說tornado能處理多個請求了,你瀏覽器該等還是得等著
協程實現異步
協程還不理解呢,還想實現異步,你就實現就行了
不用管擁護啥了,進程線程你們搞起來都麻煩呢,更別說協程了
版本1
最low的一個初級版本
'''
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Created by victor
'''
# 本模塊的功能:<># 這個就相等于一個客戶端的請求
import time
import threadinggen = None# 添加一個耗時的操作
def longIO():def run():print("開始耗時操作")time.sleep(3)try:global gengen.send("victor is wonderful!!!")except StopIteration as e:passprint("結束耗時操作")threading.Thread(target=run,).start()
# 這個longio這部分 ,就像ajax一樣都不用我們來寫了
'''
這樣就會有一個問題,這個run()函數的返回值我們接受不到
為了解決這個問題,我們需要寫一個函數,這個函數叫做回調函數
'''def reqA():print("開始處理reqA")res = yield longIO()print("接受到longIO的數據為:",res)# 這里就相當于掛起了print("結束處理reqA")# 這個就相等于另一個客戶端的請求
def reqB():print("開始處理reqB")time.sleep(1)print("結束處理reqB")def main():# 這就是同步在處理global gengen = reqA() # 生成一個生成器next(gen) # 執行reqAreqB()while True:'''# 如果你要想寫死循環,你不要直接寫死循環,你得睡一睡# 為什么要睡一睡呢,因為你要是不睡你會發現你的CPU利用率占100%'''time.sleep(0.1)if __name__ == '__main__':main()
版本2
我們有一個問題
版本1中在調用reqA的時候和reqB的調用方式可不一樣的啊
也就數不能將其視為簡單的函數,而是需要作為生成器來用,我們想的時候是當成一個普通函數來對待
現實
global gen
gen = reqA() # 生成一個生成器
next(gen) # 執行reqA
理想
reqA() # 僅僅的簡單的調用
這個時候寄需要我們的裝飾器
來登場了
''' 裝飾器還會寫么'''
def genCoroutine(func):# 這個是帶有參數的裝飾器def wapper(*args,**kwargs):# 其實說白了還是那三句話global gengen = func(*args,**kwargs) # 生成一個生成器next(gen) # 執行reqAreturn wapper
然后定義的時候
@genCoroutine
def reqA():print("開始處理reqA")res = yield longIO()print("接受到longIO的數據為:",res)# 這里就相當于掛起了print("結束處理reqA")
然后執行的時候
'''這個就相等于另一個客戶端的請求 '''
def reqB():print("開始處理reqB")time.sleep(1)print("結束處理reqB")def main():# 這就是同步在處理# global gen# gen = reqA() # 生成一個生成器# next(gen) # 執行reqAreqA()reqB()while True:'''# 如果你要想寫死循環,你不要直接寫死循環,你得睡一睡# 為什么要睡一睡呢,因為你要是不睡你會發現你的CPU利用率占100%'''time.sleep(0.1)if __name__ == '__main__':main()
版本3
其實版本2中還有一個問題,他存在一個全局的gen變量,說白了就是假裝讓他不再那塊兒
這個是最復雜版本,看看吧
- 文檔說明,導入相關模塊
'''
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Created by victor
'''
# 本模塊的功能:<>import time
import threading
- 定義裝飾器(最重要的部分,實現異步,高效,并發的原理)
def genCoroutine(func):'''這個好多人就屢不清了'''def wapper(*args, **kwargs):'''這樣的話這個裝飾器就麻煩了,因為我還得要這個全局的gen啊我需要獲得多個生成器'''gen1 = func() # reqA的生成器gen2 = next(gen1) # longIO的生成器# 在這里面創建我的線程# 掛起他def run(g):# 這個就是執行longIO去了res = next(g)try:gen1.send(res) # 返回給reqA數據except StopIteration as e:# 啥都不干passthreading.Thread(target=run, args=(gen2,)).start()return wapper
- 最難執行的部分
'''
# 添加一個耗時的操作
# handler獲取數據,(數據庫,其他服務器,循環耗時)
'''
def longIO():'''現在你只需要知道你的耗時的操作是啥,線程的東西你不用管了tornado都幫你弄好了'''print("開始耗時操作")time.sleep(3)print("結束耗時操作")# 結束耗時操作后的返回數據yield "victor is a cool man"
- 被裝飾函數定義
@genCoroutine
def reqA():print("開始處理reqA")res = yield longIO()print("接受到longIO的數據為:", res)# 這里就相當于掛起了print("結束處理reqA")
- 另一個耗時函數
''' 這個就相等于另一個客戶端的請求'''
def reqB():print("開始處理reqB")time.sleep(1)print("結束處理reqB")
- 程序入口函數
def main():reqA()reqB()while True:time.sleep(0.1)if __name__ == '__main__':main()
以后我們不用謝這么復雜的裝飾器了,tornado已經幫我們寫好了,你只要有異步就用裝飾器來裝飾一下就OK,其他的都不需要寫
tornado里面指正不是這么寫代碼,不然要是這樣寫,你們全費了,tornado留下的都是簡單易用的
這玩意不是你理解不理解,你一開始指定是不理解,你多用才能懂里面的原理
這個協程中的異步,其實他本質上不是協程,以為他用了多個線程,因為協程的定義是在一個線程里面玩的,只是來理解tornado實現原理