導航:首頁 > 研究方法 > 非同步編程使用什麼方式方法

非同步編程使用什麼方式方法

發布時間:2022-12-16 07:53:54

① Python非同步編程全攻略

如果你厭倦了多線程,不妨試試python的非同步編程,再引入async, await關鍵字之後語法變得更加簡潔和直觀,又經過幾年的生態發展,現在是一個很不錯的並發模型。

下面介紹一下python非同步編程的方方面面。

因為GIL的存在,所以Python的多線程在CPU密集的任務下顯得無力,但是對於IO密集的任務,多線程還是足以發揮多線程的優勢的,而非同步也是為了應對IO密集的任務,所以兩者是一個可以相互替代的方案,因為設計的不同,理論上非同步要比多線程快,因為非同步的花銷更少, 因為不需要額外系統申請額外的內存,而線程的創建跟系統有關,需要分配一定量的內存,一般是幾兆,比如linux默認是8MB。

雖然非同步很好,比如可以使用更少的內存,比如更好地控制並發(也許你並不這么認為:))。但是由於async/await 語法的存在導致與之前的語法有些割裂,所以需要適配,需要付出額外的努力,再者就是生態遠遠沒有同步編程強大,比如很多庫還不支持非同步,所以你需要一些額外的適配。

為了不給其他網站帶來困擾,這里首先在自己電腦啟動web服務用於測試,代碼很簡單。

本文所有依賴如下:

所有依賴可通過代碼倉庫的requirements.txt一次性安裝。

首先看一個錯誤的例子

輸出如下:

發現花費了3秒,不符合預期呀。。。。這是因為雖然用了協程,但是每個協程是串列的運行,也就是說後一個等前一個完成之後才開始,那麼這樣的非同步代碼並沒有並發,所以我們需要讓這些協程並行起來

為了讓代碼變動的不是太多,所以這里用了一個笨辦法來等待所有任務完成, 之所以在main函數中等待是為了不讓ClientSession關閉, 如果你移除了main函數中的等待代碼會發現報告異常 RuntimeError: Session is closed ,而代碼里的解決方案非常的不優雅,需要手動的等待,為了解決這個問題,我們再次改進代碼。

這里解決的方式是通過 asyncio.wait 方法等待一個協程列表,默認是等待所有協程結束後返回,會返回一個完成(done)列表,以及一個待辦(pending)列表。

如果我們不想要協程對象而是結果,那麼我們可以使用 asyncio.gather

結果輸出如下:

通過 asyncio.ensure_future 我們就能創建一個協程,跟調用一個函數差別不大,為了等待所有任務完成之後退出,我們需要使用 asyncio.wait 等方法來等待,如果只想要協程輸出的結果,我們可以使用 asyncio.gather 來獲取結果。

雖然前面能夠隨心所欲的創建協程,但是就像多線程一樣,我們也需要處理協程之間的同步問題,為了保持語法及使用情況的一致,多線程中用到的同步功能,asyncio中基本也能找到, 並且用法基本一致,不一致的地方主要是需要用非同步的關鍵字,比如 async with/ await 等

通過鎖讓並發慢下來,讓協程一個一個的運行。

輸出如下:

通過觀察很容易發現,並發的速度因為鎖而慢下來了,因為每次只有一個協程能獲得鎖,所以並發變成了串列。

通過事件來通知特定的協程開始工作,假設有一個任務是根據http響應結果選擇是否激活。

輸出如下:

可以看到事件(Event)等待者都是在得到響應內容之後輸出,並且事件(Event)可以是多個協程同時等待。

上面的事件雖然很棒,能夠在不同的協程之間同步狀態,並且也能夠一次性同步所有的等待協程,但是還不夠精細化,比如想通知指定數量的等待協程,這個時候Event就無能為力了,所以同步原語中出現了Condition。

輸出如下:

可以看到,前面兩個等待的協程是在同一時刻完成,而不是全部等待完成。

通過創建協程的數量來控制並發並不是非常優雅的方式,所以可以通過信號量的方式來控制並發。

輸出如下:

可以發現,雖然同時創建了三個協程,但是同一時刻只有兩個協程工作,而另外一個協程需要等待一個協程讓出信號量才能運行。

無論是協程還是線程,任務之間的狀態同步還是很重要的,所以有了應對各種同步機制的同步原語,因為要保證一個資源同一個時刻只能一個任務訪問,所以引入了鎖,又因為需要一個任務等待另一個任務,或者多個任務等待某個任務,因此引入了事件(Event),但是為了更精細的控制通知的程度,所以又引入了條件(Condition), 通過條件可以控制一次通知多少的任務。

有時候的並發需求是通過一個變數控制並發任務的並發數而不是通過創建協程的數量來控制並發,所以引入了信號量(Semaphore),這樣就可以在創建的協程數遠遠大於並發數的情況下讓協程在指定的並發量情況下並發。

不得不承認非同步編程相比起同步編程的生態要小的很多,所以不可能完全非同步編程,因此需要一種方式兼容。

多線程是為了兼容同步得代碼。

多進程是為了利用CPU多核的能力。

輸出如下:

可以看到總耗時1秒,說明所有的線程跟進程是同時運行的。

下面是本人使用過的一些非同步庫,僅供參考

web框架

http客戶端

資料庫

ORM

雖然非同步庫發展得還算不錯,但是中肯的說並沒有覆蓋方方面面。

雖然我鼓勵大家嘗試非同步編程,但是本文的最後卻是讓大家謹慎的選擇開發環境,如果你覺得本文的並發,同步,兼容多線程,多進程不值得一提,那麼我十分推薦你嘗試以非同步編程的方式開始一個新的項目,如果你對其中一些還有疑問或者你確定了要使用的依賴庫並且大多數是沒有非同步庫替代的,那麼我還是建議你直接按照自己擅長的同步編程開始。

非同步編程雖然很不錯,不過,也許你並不需要。

② CompletableFuture非同步編程

方式一:使用默認線程池

方式二:使用自定義線程池(建議使用)

CompletionStage介面可以清晰地描述任務之間的這種時序關系,時序關系:串列,並行,匯聚。

線程與線程之間的執行順序是串列的。

演示串列

CompletionStage介面裡面描述AND匯聚關系,主要是thenCombine、thenAcceptBoth和runAfterBoth系列的介面,這些介面的區別也是源自fn、consumer、action這三個核心參數不同。

演示:

CompletionStage介面裡面描述OR匯聚關系,主要是applyToEither、acceptEither和runAfterEither系列的 介面,這些介面的區別也是源自fn、consumer、action這三個核心參數不同。

功能演示:

**** 碼字不易如果對你有幫助請給個關注****

**** 愛技術愛生活 QQ群: 894109590****

③ javascript非同步編程有幾種方式

為了解決這個問題,Javascript語言將任務的執行模式分成兩種:同步(Synchronous)和非同步(Asynchronous)。
"同步模式"就是上一段的模式,後一個任務等待前一個任務結束,然後再執行,程序的執行順序與任務的排列順序是一致的、同步的;"非同步模式"則完全不同,每一個任務有一個或多個回調函數(callback),前一個任務結束後,不是執行後一個任務,而是執行回調函數,後一個任務則是不等前一個任務結束就執行,所以程序的執行順序與任務的排列順序是不一致的、非同步的。
"非同步模式"非常重要。在瀏覽器端,耗時很長的操作都應該非同步執行,避免瀏覽器失去響應,最好的例子就是Ajax操作。在伺服器端,"非同步模式"甚至是唯一的模式,因為執行環境是單線程的,如果允許同步執行所有http請求,伺服器性能會急劇下降,很快就會失去響應。

什麼是同步編程、非同步編程

同步編程:傳統的同步編程是一種請求響應模型,調用一個方法,等待其響應返回。就是一個線程獲得了一個任務,然後去執行這個任務, 當這個任務執行完畢後,才能執行接下來的另外一個任務。

非同步編程:非同步編程就是要重新考慮是否需要響應的問題,也就是縮小需要響應的地方。因為越快獲得響應,就是越同步化,順序化,事務化,性能差化,非同步編程通常是通過fire and forget方式實現。

(4)非同步編程使用什麼方式方法擴展閱讀:

在同步編程中,所有的操作都是順序執行的,比如從socket中讀取(請求),然後寫入(回應)到socket中,每一個操作都是阻塞的。

非同步編程的原則是,讓進程處理多個並發執行的上下文來模擬並行處理方式 ,非同步應用使用一個事件循環,當一個事件觸發暫停或恢復執行上下文:

只有一個上下文處於活動狀態,上下文之間進行輪替,代碼中的顯示指令告訴事件循環,哪裡可以暫停執行,這時,進程將查找其他待處理的線程進行恢復,最終,進程將回到函數暫停的地方繼續運行,從一個執行上下文移到另一個上下文稱為切換。

⑤ 下面哪些方法可以用作javascript非同步模式的編程

javascript語言是單線程機制。所謂單線程就是按次序執行,執行完一個任務再執行下一個。

對於瀏覽器來說,也就是無法在渲染頁面的同時執行代碼。

單線程機制的優點在於實現起來較為簡單,運行環境相對簡單。缺點在於,如果中間有任務需要響應時間過長,經常會導致

頁面載入錯誤或者瀏覽器無響應的狀況。這就是所謂的逗同步模式地,程序執行順序與任務排列順序一致。對於瀏覽器來說,

同步模式效率較低,耗時長的任務都應該使用非同步模式;而在伺服器端,非同步模式則是唯一的模式,如果採用同步模式個人認為

伺服器很快就會出現12306在高峰期的表現。。。。

非同步模式的四種方式:

1.回調函數callback

所謂回調函數,就是將函數作為參數傳到需要回調的函數內部再執行。

典型的例子就是發送ajax請求。例如:

$.ajax({

async: false,

cache: false,

dataType: 'json',

url: "url",

success: function(data) {

console.log('success');

},

error: function(data) {

console.log('error');

}

})

當發送ajax請求後,等待回應的過程不會堵塞程序運行,耗時的操作相當於延後執行。

回調函數的優點在於簡單,容易理解,但是可讀性較差,耦合度較高,不易於維護。

2.事件驅動

javascript可以稱之為是基於對象的語言,而基於對象的基本特徵就是事件驅動(Event-Driven)。

事件驅動,指的是由滑鼠和熱鍵的動作引發的一連串的程序操作。

例如,為頁面上的某個
$('#btn').onclick(function(){

console.log('click button');

});

綁定事件相當於在元素上進行監聽,是否執行注冊的事件代碼取決於事件是否發生。

優點在於容易理解,一個元素上可以綁定多個事件,有利於實現模塊化;但是缺點在於稱為事件驅動的模型後,流程不清晰。

3.發布/訂閱

發布訂閱模式(publish-subscribe pattern)又稱為觀察者模式(Observer pattern)。

該模式中,有兩類對象:觀察者和目標對象。目標對象中存在著一份觀察者的列表,當目標對象

的狀態發生改變時,主動通知觀察者,從而建立一種發布/訂閱的關系。

jquery有相關的插件,在這不是重點不細說了。。。。回頭寫個實現貼上來

4.promise模式

promise對象是CommonJS工作組提供的一種規范,用於非同步編程的統一介面。

promise對象通常實現一種then的方法,用來在注冊狀態發生改變時作為對應的回調函數。

promise模式在任何時刻都處於以下三種狀態之一:未完成(unfulfilled)、已完成(resolved)和拒絕(rejected)。以CommonJS
Promise/A
標准為例,promise對象上的then方法負責添加針對已完成和拒絕狀態下的處理函數。then方法會返回另一個promise對象,以便於形成promise管道,這種返回promise對象的方式能夠支持開發人員把非同步操作串聯起來,如then(resolvedHandler,
rejectedHandler); 。resolvedHandler
回調函數在promise對象進入完成狀態時會觸發,並傳遞結果;rejectedHandler函數會在拒絕狀態下調用。

Jquery在1.5的版本中引入了一個新的概念叫Deferred,就是CommonJS promise A標準的一種衍生。可以在jQuery中創建

$.Deferref的對象。同時也對發送ajax請求以及數據類型有了新的修改,參考JQuery API。

除了以上四種,javascript中還可以利用各種函數模擬非同步方式,更有詭異的諸如用同步調用非同步的case

只能用team里同事形容java和javascript的一句話作為結尾:

逗寫java像在高速路上開車,寫javascript像在草原上開車地-------------以此來形容javascript這種無類型的語言有多自由
but,如果草原上都是坑。

⑥ 什麼是非同步編程

傳統的同步編程是一種請求響應模型,調用一個方法,等待其響應返回.

非同步編程就是要重新考慮是否需要響應的問題,也就是縮小需要響應的地方。因為越快獲得響應,就是越同步化,順序化,事務化,性能差化。

非同步編程通常是通過fire and forget方式實現,發射事件後即忘記,做別的事情了,無需立即等待剛才發射的響應結果了。(發射事件的地方稱為生產者,而將在另外一個地方響應事件的處理者稱為消費者).非同步編程是一種事件驅動編程,需要完全改變思路,將「請求響應」的思路轉變到「事件驅動」思路上,是一種軟體編程思維的轉變.下面幾種你看參考一下

1、非同步編程模型 (APM) 模式(也稱為 IAsyncResult 模式),其中非同步操作要求 Begin 和 End 方法(例如,非同步寫操作的 BeginWrite 和 EndWrite)。對於新的開發工作不再建議採用此模式。

2、基於事件的非同步模式 (EAP) 需要一個具有 Async 後綴的方法,還需要一個或多個事件、事件處理程序、委託類型和 EventArg 派生的類型。EAP 是在 .NET Framework 2.0 版中引入的。對於新的開發工作不再建議採用此模式。
3、基於任務的非同步模式 (TAP),該模式使用一個方法表示非同步操作的啟動和完成。.NET Framework 4 中引入了 TAP,並且是 .NET Framework 中非同步編程的建議方法。

⑦ python非同步有哪些方式

yield相當於return,他將相應的值返回給調用next()或者send()的調用者,從而交出了CPU使用權,而當調用者再次調用next()或者send()的時候,又會返回到yield中斷的地方,如果send有參數,還會將參數返回給yield賦值的變數,如果沒有就和next()一樣賦值為None。但是這里會遇到一個問題,就是嵌套使用generator時外層的generator需要寫大量代碼,看如下示例:
注意以下代碼均在Python3.6上運行調試

#!/usr/bin/env python# encoding:utf-8def inner_generator():
i = 0
while True:
i = yield i if i > 10: raise StopIterationdef outer_generator():
print("do something before yield")
from_inner = 0
from_outer = 1
g = inner_generator()
g.send(None) while 1: try:
from_inner = g.send(from_outer)
from_outer = yield from_inner except StopIteration: breakdef main():
g = outer_generator()
g.send(None)
i = 0
while 1: try:
i = g.send(i + 1)
print(i) except StopIteration: breakif __name__ == '__main__':
main()041

為了簡化,在Python3.3中引入了yield from

yield from

使用yield from有兩個好處,

1、可以將main中send的參數一直返回給最里層的generator,
2、同時我們也不需要再使用while循環和send (), next()來進行迭代。

我們可以將上邊的代碼修改如下:

def inner_generator():
i = 0
while True:
i = yield i if i > 10: raise StopIterationdef outer_generator():
print("do something before coroutine start") yield from inner_generator()def main():
g = outer_generator()
g.send(None)
i = 0
while 1: try:
i = g.send(i + 1)
print(i) except StopIteration: breakif __name__ == '__main__':
main()

執行結果如下:

do something before coroutine start123456789101234567891011

這里inner_generator()中執行的代碼片段我們實際就可以認為是協程,所以總的來說邏輯圖如下:

我們都知道Python由於GIL(Global Interpreter Lock)原因,其線程效率並不高,並且在*nix系統中,創建線程的開銷並不比進程小,因此在並發操作時,多線程的效率還是受到了很大制約的。所以後來人們發現通過yield來中斷代碼片段的執行,同時交出了cpu的使用權,於是協程的概念產生了。在Python3.4正式引入了協程的概念,代碼示例如下:

import asyncio# Borrowed from http://curio.readthedocs.org/en/latest/[email protected] countdown(number, n):
while n > 0:
print('T-minus', n, '({})'.format(number)) yield from asyncio.sleep(1)
n -= 1loop = asyncio.get_event_loop()
tasks = [
asyncio.ensure_future(countdown("A", 2)),
asyncio.ensure_future(countdown("B", 3))]
loop.run_until_complete(asyncio.wait(tasks))
loop.close()12345678910111213141516

示例顯示了在Python3.4引入兩個重要概念協程和事件循環,
通過修飾符@asyncio.coroutine定義了一個協程,而通過event loop來執行tasks中所有的協程任務。之後在Python3.5引入了新的async & await語法,從而有了原生協程的概念。

async & await

在Python3.5中,引入了aync&await 語法結構,通過」aync def」可以定義一個協程代碼片段,作用類似於Python3.4中的@asyncio.coroutine修飾符,而await則相當於」yield from」。

先來看一段代碼,這個是我剛開始使用async&await語法時,寫的一段小程序。

#!/usr/bin/env python# encoding:utf-8import asyncioimport requestsimport time


async def wait_download(url):
response = await requets.get(url)
print("get {} response complete.".format(url))


async def main():
start = time.time()
await asyncio.wait([
wait_download("http://www.163.com"),
wait_download("http://www.mi.com"),
wait_download("http://www.google.com")])
end = time.time()
print("Complete in {} seconds".format(end - start))


loop = asyncio.get_event_loop()
loop.run_until_complete(main())

這里會收到這樣的報錯:

Task exception was never retrieved
future: <Task finished coro=<wait_download() done, defined at asynctest.py:9> exception=TypeError("object Response can't be used in 'await' expression",)>
Traceback (most recent call last):
File "asynctest.py", line 10, in wait_download
data = await requests.get(url)
TypeError: object Response can't be used in 'await' expression123456

這是由於requests.get()函數返回的Response對象不能用於await表達式,可是如果不能用於await,還怎麼樣來實現非同步呢?
原來Python的await表達式是類似於」yield from」的東西,但是await會去做參數檢查,它要求await表達式中的對象必須是awaitable的,那啥是awaitable呢? awaitable對象必須滿足如下條件中其中之一:

1、A native coroutine object returned from a native coroutine function .

原生協程對象

2、A generator-based coroutine object returned from a function decorated with types.coroutine() .

types.coroutine()修飾的基於生成器的協程對象,注意不是Python3.4中asyncio.coroutine

3、An object with an await method returning an iterator.

實現了await method,並在其中返回了iterator的對象

根據這些條件定義,我們可以修改代碼如下:

#!/usr/bin/env python# encoding:utf-8import asyncioimport requestsimport time


async def download(url): # 通過async def定義的函數是原生的協程對象
response = requests.get(url)
print(response.text)


async def wait_download(url):
await download(url) # 這里download(url)就是一個原生的協程對象
print("get {} data complete.".format(url))


async def main():
start = time.time()
await asyncio.wait([
wait_download("http://www.163.com"),
wait_download("http://www.mi.com"),
wait_download("http://www.google.com")])
end = time.time()
print("Complete in {} seconds".format(end - start))


loop = asyncio.get_event_loop()
loop.run_until_complete(main())27282930

好了現在一個真正的實現了非同步編程的小程序終於誕生了。
而目前更牛逼的非同步是使用uvloop或者pyuv,這兩個最新的Python庫都是libuv實現的,可以提供更加高效的event loop。

uvloop和pyuv

pyuv實現了Python2.x和3.x,但是該項目在github上已經許久沒有更新了,不知道是否還有人在維護。
uvloop只實現了3.x, 但是該項目在github上始終活躍。

它們的使用也非常簡單,以uvloop為例,只需要添加以下代碼就可以了

import asyncioimport uvloop
asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())123

閱讀全文

與非同步編程使用什麼方式方法相關的資料

熱點內容
快速去除甲醛的小方法你知道幾個 瀏覽:798
自行車架尺寸測量方法 瀏覽:118
石磨子的製作方法視頻 瀏覽:146
行善修心的正確方法 瀏覽:400
土豆燉雞湯的正確方法和步驟 瀏覽:272
北京電流檢測方法 瀏覽:481
手機u盤保護方法 瀏覽:113
數字搭配有哪些方法 瀏覽:666
約一場球的正確方法 瀏覽:187
在家中洗衣服的方法如何 瀏覽:293
28天鍛煉腹肌最快的方法 瀏覽:201
簡單練翹臀方法視頻 瀏覽:758
心理診斷評估常用的方法有哪些 瀏覽:843
什麼方法能讓手機不黑屏 瀏覽:721
電腦開機慢的處理方法視頻 瀏覽:724
後天形成內斜視訓練方法有哪些 瀏覽:361
羊脂白的鑒別方法 瀏覽:623
家常腌酸菜方法視頻 瀏覽:256
黃安倫的教學方法 瀏覽:963
做糖最簡便的方法 瀏覽:641