當前位置:首頁 » 編程語言 » c語言協程庫

c語言協程庫

發布時間: 2025-03-25 19:31:58

python2.7怎麼實現非同步

改進之前
之前,我的查詢步驟很簡單,就是:
前端提交查詢請求 --> 建立資料庫連接 --> 新建游標 --> 執行命令 --> 接受結果 --> 關閉游標、連接
這幾大步驟的順序執行。
這裡面當然問題很大:
建立資料庫連接實際上就是新建一個套接字。這是進程間通信的幾種方法里,開銷最大的了。
在「執行命令」和「接受結果」兩個步驟中,線程在阻塞在資料庫內部的運行過程中,資料庫連接和游標都處於閑置狀態。
這樣一來,每一次查詢都要順序的新建資料庫連接,都要阻塞在資料庫返回結果的過程中。當前端提交大量查詢請求時,查詢效率肯定是很低的。
第一次改進
之前的模塊里,問題最大的就是第一步——建立資料庫連接套接字了。如果能夠一次性建立連接,之後查詢能夠反復服用這個連接就好了。
所以,首先應該把資料庫查詢模塊作為一個單獨的守護進程去執行,而前端app作為主進程響應用戶的點擊操作。那麼兩條進程怎麼傳遞消息呢?翻了幾天Python文檔,終於構思出來:用隊列queue作為生產者(web前端)向消費者(資料庫後端)傳遞任務的渠道。生產者,會與SQL命令一起,同時傳遞一個管道pipe的連接對象,作為任務完成後,回傳結果的渠道。確保,任務的接收方與發送方保持一致。
作為第二個問題的解決方法,可以使用線程池來並發獲取任務隊列中的task,然後執行命令並回傳結果。
第二次改進
第一次改進的效果還是很明顯的,不用任何測試手段。直接點擊頁面鏈接,可以很直觀地感覺到反應速度有很明顯的加快。
但是對於第二個問題,使用線程池還是有些欠妥當。因為,CPython解釋器存在GIL問題,所有線程實際上都在一個解釋器進程里調度。線程稍微開多一點,解釋器進程就會頻繁的切換線程,而線程切換的開銷也不小。線程多一點,甚至會出現「抖動」問題(也就是剛剛喚醒一個線程,就進入掛起狀態,剛剛換到棧幀或內存的上下文,又被換回內存或者磁碟),效率大大降低。也就是說,線程池的並發量很有限。
試過了多進程、多線程,只能在單個線程里做文章了。
Python中的asyncio庫
Python里有大量的協程庫可以實現單線程內的並發操作,比如Twisted、Gevent等等。Python官方在3.5版本里提供了asyncio庫同樣可以實現協程並發。asyncio庫大大降低了Python中協程的實現難度,就像定義普通函數那樣就可以了,只是要在def前面多加一個async關鍵詞。async def函數中,需要阻塞在其他async def函數的位置前面可以加上await關鍵詞。
import asyncio
async def wait():
await asyncio.sleep(2)
async def execute(task):
process_task(task)
await wait()
continue_job()
async def函數的執行稍微麻煩點。需要首先獲取一個loop對象,然後由這個對象代為執行async def函數。
loop = asyncio.get_event_loop()
loop.run_until_complete(execute(task))
loop.close()
loop在執行execute(task)函數時,如果遇到await關鍵字,就會暫時掛起當前協程,轉而去執行其他阻塞在await關鍵詞的協程,從而實現協程並發。
不過需要注意的是,run_until_complete()函數本身是一個阻塞函數。也就是說,當前線程會等候一個run_until_complete()函數執行完畢之後,才會繼續執行下一部函數。所以下面這段代碼並不能並發執行。
for task in task_list:
loop.run_until_complete(task)
對與這個問題,asyncio庫也有相應的解決方案:gather函數。
loop = asyncio.get_event_loop()
tasks = [asyncio.ensure_future(execute(task))
for task in task_list]
loop.run_until_complete(asyncio.gather(*tasks))
loop.close()
當然了,async def函數的執行並不只有這兩種解決方案,還有call_soon與run_forever的配合執行等等,更多內容還請參考官方文檔。
Python下的I/O多路復用
協程,實際上,也存在上下文切換,只不過開銷很輕微。而I/O多路復用則完全不存在這個問題。
目前,Linux上比較火的I/O多路復用API要算epoll了。Tornado,就是通過調用C語言封裝的epoll庫,成功解決了C10K問題(當然還有Pypy的功勞)。
在Linux里查文檔,可以看到epoll只有三類函數,調用起來比較方便易懂。
創建epoll對象,並返回其對應的文件描述符(file descriptor)。
int epoll_create(int size);
int epoll_create1(int flags);
控制監聽事件。第一個參數epfd就對應於前面命令創建的epoll對象的文件描述符;第二個參數表示該命令要執行的動作:監聽事件的新增、修改或者刪除;第三個參數,是要監聽的文件對應的描述符;第四個,代表要監聽的事件。
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
等候。這是一個阻塞函數,調用者會等候內核通知所注冊的事件被觸發。
int epoll_wait(int epfd, struct epoll_event *events,
int maxevents, int timeout);
int epoll_pwait(int epfd, struct epoll_event *events,
int maxevents, int timeout,
const sigset_t *sigmask);
在Python的select庫里:
select.epoll()對應於第一類創建函數;
epoll.register(),epoll.unregister(),epoll.modify()均是對控制函數epoll_ctl的封裝;
epoll.poll()則是對等候函數epoll_wait的封裝。
Python里epoll相關API的最大問題應該是在epoll.poll()。相比於其所封裝的epoll_wait,用戶無法手動指定要等候的事件,也就是後者的第二個參數struct epoll_event *events。沒法實現精確控制。因此只能使用替代方案:select.select()函數。
根據Python官方文檔,select.select(rlist, wlist, xlist[, timeout])是對Unix系統中select函數的直接調用,與C語言API的傳參很接近。前三個參數都是列表,其中的元素都是要注冊到內核的文件描述符。如果想用自定義類,就要確保實現了fileno()方法。
其分別對應於:
rlist: 等候直到可讀
wlist: 等候直到可寫
xlist: 等候直到異常。這個異常的定義,要查看系統文檔。
select.select(),類似於epoll.poll(),先注冊文件和事件,然後保持等候內核通知,是阻塞函數。
實際應用
Psycopg2庫支持對非同步和協程,但和一般情況下的用法略有區別。普通資料庫連接支持不同線程中的不同游標並發查詢;而非同步連接則不支持不同游標的同時查詢。所以非同步連接的不同游標之間必須使用I/O復用方法來協調調度。
所以,我的大致實現思路是這樣的:首先並發執行大量協程,從任務隊列中提取任務,再向連接池請求連接,創建游標,然後執行命令,並返回結果。在獲取游標和接受查詢結果之前,均要阻塞等候內核通知連接可用。
其中,連接池返回連接時,會根據引用連接的協程數量,返回負載最輕的連接。這也是自己定義AsyncConnectionPool類的目的。
我的代碼位於:bottle-blog/dbservice.py
存在問題
當然了,這個流程目前還一些問題。
首先就是每次輪詢拿到任務之後,都會走這么一個流程。
獲取連接 --> 新建游標 --> 執行任務 --> 關閉游標 --> 取消連接引用
本來,最好的情況應該是:在輪詢之前,就建好游標;在輪詢時,直接等候內核通知,執行相應任務。這樣可以減少輪詢時的任務量。但是如果協程提前對應好連接,那就不能保證在獲取任務時,保持各連接負載均衡了。
所以這一塊,還有工作要做。
還有就是epoll沒能用上,有些遺憾。
以後打算寫點C語言的內容,或者用Python/C API,或者用Ctypes包裝共享庫,來實現epoll的調用。
最後,請允許我吐槽一下Python的epoll相關文檔:簡直太弱了!!!必須看源碼才能弄清楚功能。

⑵ C++學習完整學習路線及方向指引,保你少走彎路

C++的完整學習路線及方向指引如下:

學習路線

  1. 階段一:C語言開發

    • 學習目標:具備C/C++領域基礎專業編程能力,能獨立完成項目案例。
    • 知識點:C語言概述、編程基礎、函數、指針、內存管理、復合類型、文件操作。
  2. 階段二:C高級編程

    • 知識點:內存分區、函數調用模型、指針高級、函數指針與回調函數、數據結構與鏈表、遞歸函數、數據結構與演算法、介面的封裝和設計。
  3. 階段三:C++核心編程

    • 學習目標:掌握常用C++方向基本知識和常用技能。
    • 知識點:C++對C的擴展、引用、函數重載、類和對象、對象的動態分配和釋放、運算符重載、類的繼承和派生、多態、面向抽象類編程與設計模式、函數模板、類模板、異常、V/O流、STL。
  4. 階段四:C++新特性

    • 學習目標:掌握C++11/14/17/20/23常用新特性。
    • 知識點:關鍵字、STL容器、智能指針、正則表達式、線程、協程、原子操作、lambda表達式。
  5. 階段五:Linux工程管理

    • 學習目標:掌握Makefile,掌握git/svn版本管理工具。
    • 知識點:Makefile、分布式版本控制git、Linux系統運行時參數命令。
  6. 階段六:Linux系統編程

    • 學習目標:熟悉Linux開發環境,熟練使用Linux系統介面,掌握進程間通信。
    • 知識點:Linux命令、Linux開發與調試工具、系統I/O操作、進程與IPC通信、線程與並發同步。
  7. 階段七:Linux網路編程

    • 學習目標:熟悉網路基礎知識,掌握TCP/IP協議棧,高並發伺服器開發。
    • 知識點:網路協議與網路編程、高並發伺服器開發。
  8. 階段八:中間件開發

    • 學習目標:掌握資料庫操作,掌握中間件。
    • 知識點:MySQL資料庫編程、Redis緩存資料庫編程、Nginx搭建與使用、MongoDB資料庫編程。

學習方向

  • C++企業級桌面應用開發:專注於桌面應用程序的開發,如辦公軟體、圖形界面應用等。
  • 音視頻流媒體:涉及音視頻處理、流媒體傳輸等領域,如視頻播放器、直播軟體等。
  • Linux C++後台伺服器:專注於Linux環境下的後台伺服器開發,如Web伺服器、游戲伺服器等。
  • Linux內核源碼:深入學習和研究Linux內核源碼,為內核開發或優化提供支持。
  • 網路安防:涉及網路安全、防火牆、入侵檢測等領域,如安全軟體、防火牆系統等。
  • 游戲開發:專注於游戲引擎的開發、游戲邏輯的實現等,如PC游戲、手機游戲等。
  • 嵌入式開發:涉及嵌入式系統的開發,如智能家居、物聯網設備等。

按照上述學習路線和方向進行規劃,可以系統地學習C++,並逐步提升自己的編程能力。在學習過程中,注重理論與實踐相結合,多做項目實踐,以加深理解和應用。

熱點內容
oracle的linux客戶端配置 發布:2025-03-26 05:36:38 瀏覽:200
安卓app安裝包在哪個目錄 發布:2025-03-26 05:35:43 瀏覽:662
安卓愛奇藝觀看影片時如何看高清 發布:2025-03-26 05:34:13 瀏覽:532
換安卓手機如何把通訊錄 發布:2025-03-26 05:30:41 瀏覽:348
c語言的環境變數 發布:2025-03-26 05:20:57 瀏覽:957
哪個牌子的安卓手機界面好看 發布:2025-03-26 05:10:11 瀏覽:977
小樹茶存儲 發布:2025-03-26 05:04:56 瀏覽:572
pt上傳慢 發布:2025-03-26 04:31:17 瀏覽:54
阿里雲伺服器哪個好用 發布:2025-03-26 04:26:09 瀏覽:242
windows編程技術 發布:2025-03-26 04:19:47 瀏覽:422